WDS2 Lesson 18 3 device drivers use memory analog disk elevator algorithm to read and write partition formatting

Posted by jonemo on Thu, 10 Feb 2022 09:00:07 +0100


reference resources drivers\block\xd.c and drivers\block\z2ram.c

ls /dev/sd* -l view all disk devices. The disk with secondary device number 0 represents the whole disk, and the device numbers 1, 2 and 3 represent the partitions of the disk.

Program 1 uses memory to simulate disk to simply process request do_ramblock_request

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>

static struct gendisk *ramblock_disk;
static request_queue_t *ramblock_queue; // queue
static DEFINE_SPINLOCK(ramblock_lock);  // Spin lock
static int major;
static struct block_device_operations ramdisk_fops = {
	.owner	= THIS_MODULE,
};
#define RAMBLOCK_SIZE (1024*1024) // 1M

static void do_ramblock_request(request_queue_t * q)
{
    static int cnt = 0;
    printk("do_ramblock_request: %d\n", ++cnt);

    // To prevent jamming after insmod, take out the request here and pretend that the processing is completed
    struct request *req;
    while ((req = elv_next_request(q)) != NULL) { // Take out the next request with elevator scheduling algorithm
        end_request(req, 1);    // It is considered that the request processing has been completed. 1 succeeded and 0 failed
    }
}

static int ramblock_init(void)
{
    // 1. Assign a gendisk structure
    ramblock_disk = alloc_disk(16); // Number of secondary equipment number partitions up to 15 partitions
    // 2. Setting
    // 2.1 allocate / set queue: provide read-write function
        // You need to provide a function to process the queue
    ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
    ramblock_disk->queue = ramblock_queue; 
    // 2.2 set other attributes, such as capacity
    major = register_blkdev(0, "ramdisk"); // Registration, only the master device number and name, no fops
    ramblock_disk->major        = major;
    ramblock_disk->first_minor  = 0; // 15 secondary devices starting from 0
    sprintf(ramblock_disk->disk_name, "ramdisk");
    ramblock_disk->fops         = ramdisk_fops;
    set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512); // Set the capacity of 1M in sectors
                                // In the kernel file system, the sector is considered to be 512Byte 
    // 3. Registration
    add_disk(ramblock_disk);

    return 0;
}
static void ramblock_exit(void)
{
    unregister_blkdev(major, "ramdisk");
    del_gendisk(ramblock_disk);
    put_disk(ramblock_disk);
    blk_cleanup_queue(ramblock_queue);
}
module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");

Test program 1 simple view block device information

Program 2 allocates memory to copy data between the analog disk and req - > buffer

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>

static struct gendisk *ramblock_disk;
static request_queue_t *ramblock_queue; // queue
static DEFINE_SPINLOCK(ramblock_lock);  // Spin lock
static int major;
static struct block_device_operations ramdisk_fops = {
	.owner	= THIS_MODULE,
};
#define RAMBLOCK_SIZE (1024*1024) // 1M
static unsigned char *ramblock_buf; // Allocated memory

static void do_ramblock_request(request_queue_t * q)
{
    // static int cnt = 0;
    // printk("do_ramblock_request: %d\n", ++cnt);

    // Simulate the block device with memory and copy the data in the request
    struct request *req; // Process the request after it is taken out
    while ((req = elv_next_request(q)) != NULL) { // Take out the next request with elevator scheduling algorithm
        // Three elements of data transmission: source destination length
        unsigned long offset = req->sector << 9; // *512 the size of one sector of the source is 512 and restored to Byte
        unsigned long len  = req->current_nr_sectors << 9; // *512 length
        if (rq_data_dir(req) == READ) { // Read request
            memcpy(req->buffer, ramblock_buf+offset, len); // Read data from disk memory simulated disk
        } else {
            memcpy(ramblock_buf+offset, req->buffer, len); // Write buffer data to disk memory
        }

        end_request(req, 1);    // Request processing has been completed. 1 succeeded. 0 failed
    }
}

static int ramblock_init(void)
{
    // 1. Assign a gendisk structure
    ramblock_disk = alloc_disk(16); // Number of secondary equipment number partitions up to 15 partitions
    // 2. Setting
    // 2.1 allocate / set queue: provide read-write function
        // You need to provide a function to process the queue
    ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
    ramblock_disk->queue = ramblock_queue; 
    // 2.2 set other attributes, such as capacity
    major = register_blkdev(0, "ramdisk"); // Registration, only the master device number and name, no fops
    ramblock_disk->major        = major;
    ramblock_disk->first_minor  = 0; // 15 secondary devices starting from 0
    sprintf(ramblock_disk->disk_name, "ramdisk");
    ramblock_disk->fops         = ramdisk_fops;
    set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512); // Set the capacity of 1M in sectors
                                // In the kernel file system, the sector is considered to be 512Byte 
    
    // 3. Hardware related operations
    ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL); // Allocate a block of memory
    // 4. Registration
    add_disk(ramblock_disk);
    
    return 0;
}
static void ramblock_exit(void)
{
    unregister_blkdev(major, "ramdisk");
    del_gendisk(ramblock_disk);
    put_disk(ramblock_disk);
    blk_cleanup_queue(ramblock_queue);
    kfree(ramblock_buf);
}
module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");

Test procedure 2

  1. insmod ramblock.ko load new block
    Prompt unknown partition table after loading because ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL); The allocated memory is cleared, not the partition table. There is nothing.
  2. Format mkdosfs /dev/ramblock
  3. mount /dev/ramblock /tmp/
  4. Read and write files, cd /tmp / and then VI 1 Txt add content hello; Copy the file cp /etc/inittab
  5. cd / umount /tmp/
  6. Reconnect mount /dev/ramblock /tmp / to see if the two files are still in ls /tmp/
  7. Put the whole disk into the Bin file, cat / dev / ramblock > / MNT / ramblock bin
  8. View on ram pc.block bin,sudo mount -o loop ramblock.bin /mnt loop attaches a normal file as a block device

Program 3 adds printing when reading and writing

Just print on program 2,

static void do_ramblock_request(request_queue_t * q)
{
    static int rd_cnt = 0;
    static int wt_cnt = 0;
    // printk("do_ramblock_request: %d\n", ++cnt);

    // Simulate the block device with memory and copy the data in the request
    struct request *req; // Process the request after it is taken out
    while ((req = elv_next_request(q)) != NULL) { // Take out the next request with elevator scheduling algorithm
        // Three elements of data transmission: source destination length
        unsigned long offset = req->sector << 9; // *512 the size of one sector of the source is 512 and restored to Byte
        unsigned long len  = req->current_nr_sectors << 9; // *512 length
        if (rq_data_dir(req) == READ) { // Read request
            printk("do_ramblock_request read: %d\n", ++rd_cnt);
            memcpy(req->buffer, ramblock_buf+offset, len); // Read data from disk memory simulated disk
        } else {
            printk("do_ramblock_request write: %d\n", ++wt_cnt);
            memcpy(ramblock_buf+offset, req->buffer, len); // Write buffer data to disk memory
        }

        end_request(req, 1);    // Request processing has been completed. 1 succeeded. 0 failed
    }
}

Procedure 3 test

  1. insmod xxx.ko
  2. Format mkdosfs /dev/ramblock is write
  3. mount /dev/ramblock /tmp is all read
  4. Copy a file cp /etc/inittab /tmp. It is read but not written. It will be written later. The hard disk is not really finished when the read-write prompt is completed.
  5. Copy another file CP / etc / init D / RCS / tmp does not write immediately. Write immediately after sync. Sync is a system call

Either read or write, elevator scheduling algorithm,

Procedure 3 is then tested

Partition / dev/ramblock. Unknown value for: cylinders occurs. In order to be compatible with old devices, you need to set the number of virtual cylinders and obtain geometric properties,

On the basis of program 3, remove the printing information and add the code to obtain the geometric properties of the disk,

// Provides geometric information about the disk
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
    // Capacity = heads x cylinders x sectors x 512
	geo->heads = 2;     // How many sides of the head
	geo->sectors = 32;  // How many rings on the cylinder
	geo->cylinders = RAMBLOCK_SIZE/2/32/512; // How many sectors are there in a ring 1024 * 1024 / 2 / 512 / 32
	return 0;
}
static struct block_device_operations ramdisk_fops = {
	.owner	= THIS_MODULE,
    .getgeo = ramblock_getgeo, // Obtain geometric information and provide cylinders when partitioning
};
  1. insmod xxx.ko
  2. ls /dev/ramblock* -l has only one whole disk
  3. Partition fdsk/dev/ramblock

summary

If the future disk can be accessed like memory, the device will be simple.
Write your own queue processing function, ramblock_ queue = blk_ init_ queue(do_ramblock_request, &ramblock_lock);.