diff --git a/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/kernel/Kbuild b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/kernel/Kbuild new file mode 100644 index 00000000000000..4f1b616bf92028 --- /dev/null +++ b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/kernel/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -Wall -g -Wno-unused + +obj-m = ram-disk.o diff --git a/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/kernel/ram-disk.c b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/kernel/ram-disk.c new file mode 100644 index 00000000000000..f927054814a61b --- /dev/null +++ b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/kernel/ram-disk.c @@ -0,0 +1,258 @@ +/* + * SO2 - Block device drivers lab (#7) + * Linux - Exercise #1, #2, #3, #6 (RAM Disk) + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Simple RAM Disk"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + + +#define KERN_LOG_LEVEL KERN_ALERT + +#define MY_BLOCK_MAJOR 240 +#define MY_BLKDEV_NAME "mybdev" +#define MY_BLOCK_MINORS 1 +#define NR_SECTORS 128 + +#define KERNEL_SECTOR_SIZE 512 + +/* TODO 6: use bios for read/write requests */ +#define USE_BIO_TRANSFER 1 + + +static struct my_block_dev { + struct blk_mq_tag_set tag_set; + struct request_queue *queue; + struct gendisk *gd; + u8 *data; + size_t size; +} g_dev; + +static int my_block_open(struct block_device *bdev, fmode_t mode) +{ + return 0; +} + +static void my_block_release(struct gendisk *gd, fmode_t mode) +{ +} + +static const struct block_device_operations my_block_ops = { + .owner = THIS_MODULE, + .open = my_block_open, + .release = my_block_release +}; + +static void my_block_transfer(struct my_block_dev *dev, sector_t sector, + unsigned long len, char *buffer, int dir) +{ + unsigned long offset = sector * KERNEL_SECTOR_SIZE; + + /* check for read/write beyond end of block device */ + if ((offset + len) > dev->size) + return; + + /* TODO 3: read/write to dev buffer depending on dir */ + if (dir == 0) { + pr_err("read\n"); + memcpy(buffer, dev->data + offset, len); + } else { + if (dir == 1) { + pr_err("write\n"); + memcpy(dev->data + offset, buffer, len); + } else { + return; + } + } +} + +/* to transfer data using bio structures enable USE_BIO_TRANFER */ +#if USE_BIO_TRANSFER == 1 +static void my_xfer_request(struct my_block_dev *dev, struct request *req) +{ + /* TODO 6: iterate segments */ + struct bio_vec bvec; + struct req_iterator iter; + + rq_for_each_segment(bvec, req, iter) { + /* TODO 6: copy bio data to device buffer */ + sector_t sector = iter.iter.bi_sector; + char *buffer = kmap_atomic(bvec.bv_page); + unsigned long offset = bvec.bv_offset; + size_t len = bvec.bv_len; + int dir = bio_data_dir(iter.bio); + + my_block_transfer(dev, sector, len, buffer + offset, dir); + + kunmap_atomic(buffer); + } +} +#endif + +static blk_status_t my_block_request(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) +{ + struct request *rq; + struct my_block_dev *dev = hctx->queue->queuedata; + + /* TODO 2: get pointer to request */ + rq = bd->rq; + /* TODO 2: start request processing. */ + blk_mq_start_request(rq); + /* TODO 2: check fs request. Return if passthrough. */ + if (blk_rq_is_passthrough(rq)) { + /* TODO 2: print request information */ + printk(KERN_NOTICE "Skip non-fs request\n"); + blk_mq_end_request(rq, BLK_STS_IOERR); + goto out; + } + + pr_info("Request received:\n"); + pr_info("Start of sector: %llu\n", blk_rq_pos(rq)); + pr_info("Total size: %u\n", blk_rq_bytes(rq)); + pr_info("Direction: %hu\n", rq_data_dir(rq)); + +#if USE_BIO_TRANSFER == 1 + /* TODO 6: process the request by calling my_xfer_request */ + +#else + /* TODO 3: process the request by calling my_block_transfer */ + my_block_transfer(dev, blk_rq_pos(rq), blk_rq_bytes(rq), bio_data(rq->bio), rq_data_dir(rq)); +#endif + + /* TODO 2: end request successfully */ + blk_mq_end_request(rq, BLK_STS_OK); +out: + return BLK_STS_OK; +} + +static struct blk_mq_ops my_queue_ops = { + .queue_rq = my_block_request, +}; + +static int create_block_device(struct my_block_dev *dev) +{ + int err; + + dev->size = NR_SECTORS * KERNEL_SECTOR_SIZE; + dev->data = vmalloc(dev->size); + if (dev->data == NULL) { + printk(KERN_ERR "vmalloc: out of memory\n"); + err = -ENOMEM; + goto out_vmalloc; + } + + /* Initialize tag set. */ + dev->tag_set.ops = &my_queue_ops; + dev->tag_set.nr_hw_queues = 1; + dev->tag_set.queue_depth = 128; + dev->tag_set.numa_node = NUMA_NO_NODE; + dev->tag_set.cmd_size = 0; + dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; + err = blk_mq_alloc_tag_set(&dev->tag_set); + if (err) { + printk(KERN_ERR "blk_mq_alloc_tag_set: can't allocate tag set\n"); + goto out_alloc_tag_set; + } + + /* Allocate queue. */ + dev->queue = blk_mq_init_queue(&dev->tag_set); + if (IS_ERR(dev->queue)) { + printk(KERN_ERR "blk_mq_init_queue: out of memory\n"); + err = -ENOMEM; + goto out_blk_init; + } + blk_queue_logical_block_size(dev->queue, KERNEL_SECTOR_SIZE); + dev->queue->queuedata = dev; + + /* initialize the gendisk structure */ + dev->gd = alloc_disk(MY_BLOCK_MINORS); + if (!dev->gd) { + printk(KERN_ERR "alloc_disk: failure\n"); + err = -ENOMEM; + goto out_alloc_disk; + } + + dev->gd->major = MY_BLOCK_MAJOR; + dev->gd->first_minor = 0; + dev->gd->fops = &my_block_ops; + dev->gd->queue = dev->queue; + dev->gd->private_data = dev; + snprintf(dev->gd->disk_name, DISK_NAME_LEN, "myblock"); + set_capacity(dev->gd, NR_SECTORS); + + add_disk(dev->gd); + + return 0; + +out_alloc_disk: + blk_cleanup_queue(dev->queue); +out_blk_init: + blk_mq_free_tag_set(&dev->tag_set); +out_alloc_tag_set: + vfree(dev->data); +out_vmalloc: + return err; +} + +static void delete_block_device(struct my_block_dev *dev) +{ + if (dev->gd) { + del_gendisk(dev->gd); + put_disk(dev->gd); + } + + if (dev->queue) + blk_cleanup_queue(dev->queue); + if (dev->tag_set.tags) + blk_mq_free_tag_set(&dev->tag_set); + if (dev->data) + vfree(dev->data); +} + +static int __init my_block_init(void) +{ + int err = 0; + + /* TODO 1: register block device */ + err = register_blkdev(MY_BLOCK_MAJOR, MY_BLKDEV_NAME); + if (err < 0) { + pr_err("unable to register mybdev block device\n"); + return -EBUSY; + } + /* TODO 2: create block device using create_block_device */ + create_block_device(&g_dev); + + return 0; + +out: + /* TODO 2: unregister block device in case of an error */ + delete_block_device(&g_dev); + return err; +} + +static void __exit my_block_exit(void) +{ + /* TODO 2: cleanup block device using delete_block_device */ + delete_block_device(&g_dev); + /* TODO 1: unregister block device */ + unregister_blkdev(MY_BLOCK_MAJOR, MY_BLKDEV_NAME); + +} + +module_init(my_block_init); +module_exit(my_block_exit); diff --git a/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/.gitignore b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/.gitignore new file mode 100644 index 00000000000000..1330ce0fdf63c7 --- /dev/null +++ b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/.gitignore @@ -0,0 +1 @@ +/ram-disk-test diff --git a/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/Makefile b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/Makefile new file mode 100644 index 00000000000000..a653ce1e2faf79 --- /dev/null +++ b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/Makefile @@ -0,0 +1,8 @@ +CFLAGS = -Wall -g -m32 -static + +all: ram-disk-test + +.PHONY: clean + +clean: + -rm -f *~ *.o ram-disk-test diff --git a/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/ram-disk-test.c b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/ram-disk-test.c new file mode 100644 index 00000000000000..a6d2b3a3214b8f --- /dev/null +++ b/tools/labs/skels/block_device_drivers/1-2-3-6-ram-disk/user/ram-disk-test.c @@ -0,0 +1,89 @@ +/* + * SO2 - Block device driver (#8) + * Test suite for exercise #3 (RAM Disk) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_SECTORS 128 +#define SECTOR_SIZE 512 + +#define DEVICE_NAME "/dev/myblock" +#define MODULE_NAME "ram-disk" +#define MY_BLOCK_MAJOR "240" +#define MY_BLOCK_MINOR "0" + + +#define max_elem_value(elem) \ + (1 << 8*sizeof(elem)) + +static unsigned char buffer[SECTOR_SIZE]; +static unsigned char buffer_copy[SECTOR_SIZE]; + +static void test_sector(int fd, size_t sector) +{ + int i; + + for (i = 0; i < sizeof(buffer) / sizeof(buffer[0]); i++) + buffer[i] = rand() % max_elem_value(buffer[0]); + + lseek(fd, sector * SECTOR_SIZE, SEEK_SET); + write(fd, buffer, sizeof(buffer)); + + fsync(fd); + + lseek(fd, sector * SECTOR_SIZE, SEEK_SET); + read(fd, buffer_copy, sizeof(buffer_copy)); + + printf("test sector %3d ... ", sector); + if (memcmp(buffer, buffer_copy, sizeof(buffer_copy)) == 0) + printf("passed\n"); + else + printf("failed\n"); +} + +int main(void) +{ + int fd; + size_t i; + int back_errno; + + printf("insmod ../kernel/" MODULE_NAME ".ko\n"); + if (system("insmod ../kernel/" MODULE_NAME ".ko\n")) { + fprintf(stderr, "insmod failed\n"); + exit(EXIT_FAILURE); + } + + sleep(1); + + printf("mknod " DEVICE_NAME " b " MY_BLOCK_MAJOR " " MY_BLOCK_MINOR "\n"); + system("mknod " DEVICE_NAME " b " MY_BLOCK_MAJOR " " MY_BLOCK_MINOR "\n"); + sleep(1); + + fd = open(DEVICE_NAME, O_RDWR); + if (fd < 0) { + back_errno = errno; + perror("open"); + fprintf(stderr, "errno is %d\n", back_errno); + exit(EXIT_FAILURE); + } + + srand(time(NULL)); + for (i = 0; i < NR_SECTORS; i++) + test_sector(fd, i); + + close(fd); + + sleep(1); + printf("rmmod " MODULE_NAME "\n"); + system("rmmod " MODULE_NAME "\n"); + + return 0; +} diff --git a/tools/labs/skels/block_device_drivers/4-5-relay-disk/Kbuild b/tools/labs/skels/block_device_drivers/4-5-relay-disk/Kbuild new file mode 100644 index 00000000000000..222ee815adcb9e --- /dev/null +++ b/tools/labs/skels/block_device_drivers/4-5-relay-disk/Kbuild @@ -0,0 +1,3 @@ +EXTRA_CFLAGS = -Wall -g -Wno-unused + +obj-m = relay-disk.o diff --git a/tools/labs/skels/block_device_drivers/4-5-relay-disk/relay-disk.c b/tools/labs/skels/block_device_drivers/4-5-relay-disk/relay-disk.c new file mode 100644 index 00000000000000..98d7ff32f91bc6 --- /dev/null +++ b/tools/labs/skels/block_device_drivers/4-5-relay-disk/relay-disk.c @@ -0,0 +1,101 @@ +/* + * SO2 Lab - Block device drivers (#7) + * Linux - Exercise #4, #5 (Relay disk - bio) + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("SO2"); +MODULE_DESCRIPTION("Relay disk"); +MODULE_LICENSE("GPL"); + +#define KERN_LOG_LEVEL KERN_ALERT + +#define PHYSICAL_DISK_NAME "/dev/vdb" +#define KERNEL_SECTOR_SIZE 512 + +#define BIO_WRITE_MESSAGE "HELLOOOO" +#define BUFSIZ 512 + +/* pointer to physical device structure */ +static struct block_device *phys_bdev; + +static void send_test_bio(struct block_device *bdev, int dir) +{ + struct bio *bio = bio_alloc(GFP_NOIO, 1); + struct page *page; + char *buf; + + /* TODO 4: fill bio (bdev, sector, direction) */ + bio->bi_disk = bdev->bd_disk; + bio->bi_iter.bi_sector = 0; + bio->bi_opf = REQ_OP_READ; + + page = alloc_page(GFP_NOIO); + bio_add_page(bio, page, KERNEL_SECTOR_SIZE, 0); + + /* TODO 5: write message to bio buffer if direction is write */ + + if (dir == REQ_OP_WRITE) { + bio->bi_opf = REQ_OP_WRITE; + buf = kmap_atomic(page); + memcpy(buf, BIO_WRITE_MESSAGE, strlen(BIO_WRITE_MESSAGE)); + kunmap_atomic(buf); + } + + /* TODO 4: submit bio and wait for completion */ + submit_bio_wait(bio); + + /* TODO 4: read data (first 3 bytes) from bio buffer and print it */ + buf = kmap_atomic(page); + pr_info("%02x\t%02x\t%02x\n", buf[0], buf[1], buf[2]); + kunmap_atomic(buf); + + bio_put(bio); + __free_page(page); +} + +static struct block_device *open_disk(char *name) +{ + struct block_device *bdev; + + /* TODO 4: get block device in exclusive mode */ + pr_info("Name: %s\n", name); + bdev = blkdev_get_by_path(name, FMODE_READ | FMODE_WRITE | FMODE_EXCL, THIS_MODULE); + return bdev; +} + +static int __init relay_init(void) +{ + phys_bdev = open_disk(PHYSICAL_DISK_NAME); + if (phys_bdev == NULL) { + printk(KERN_ERR "[relay_init] No such device\n"); + return -EINVAL; + } + + send_test_bio(phys_bdev, REQ_OP_READ); + + return 0; +} + +static void close_disk(struct block_device *bdev) +{ + /* TODO 4: put block device */ + blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); +} + +static void __exit relay_exit(void) +{ + /* TODO 5: send test write bio */ + send_test_bio(phys_bdev, REQ_OP_WRITE); + close_disk(phys_bdev); +} + +module_init(relay_init); +module_exit(relay_exit); diff --git a/tools/labs/skels/block_device_drivers/4-5-relay-disk/test-relay-disk b/tools/labs/skels/block_device_drivers/4-5-relay-disk/test-relay-disk new file mode 100755 index 00000000000000..f73aad004ea50c --- /dev/null +++ b/tools/labs/skels/block_device_drivers/4-5-relay-disk/test-relay-disk @@ -0,0 +1,20 @@ +#!/bin/sh + +PHYSICAL_DISK_NAME="/dev/vdb" +TMP_FILE="/tmp/disk_data" +echo "abc" > "$PHYSICAL_DISK_NAME" +if ! insmod relay-disk.ko +then + echo "insmod failed" + exit 1 +fi +if ! rmmod relay-disk +then + echo "rmmod failed" + exit 1 +fi +sleep 1 + +echo -n "read from $PHYSICAL_DISK_NAME: " +dd if=$PHYSICAL_DISK_NAME of=$TMP_FILE count=3 bs=1 &> /dev/null +cat $TMP_FILE | hexdump -v -e '/1 "%02X "'; echo