Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
EXTRA_CFLAGS = -Wall -g -Wno-unused

obj-m = ram-disk.o
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* SO2 - Block device drivers lab (#7)
* Linux - Exercise #1, #2, #3, #6 (RAM Disk)
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/genhd.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/blk_types.h>
#include <linux/blkdev.h>
#include <linux/blk-mq.h>
#include <linux/bio.h>
#include <linux/vmalloc.h>

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);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/ram-disk-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CFLAGS = -Wall -g -m32 -static

all: ram-disk-test

.PHONY: clean

clean:
-rm -f *~ *.o ram-disk-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* SO2 - Block device driver (#8)
* Test suite for exercise #3 (RAM Disk)
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>

#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;
}
3 changes: 3 additions & 0 deletions tools/labs/skels/block_device_drivers/4-5-relay-disk/Kbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
EXTRA_CFLAGS = -Wall -g -Wno-unused

obj-m = relay-disk.o
Loading