首页 > 系统相关 >linux驱动移植-linux块设备驱动z2ram

linux驱动移植-linux块设备驱动z2ram

时间:2022-09-25 21:45:59浏览次数:82  
标签:map Z2RAM queue z2ram linux 驱动 z2 size

linux内核将块设备相关的驱动放在drivers/block路径下:

root@zhengyang:/work/sambashare/linux-5.2.8# ls drivers/block/
amiflop.c  built-in.a    loop.c    nbd.c             pktcdvd.c    rsxx         swim_asm.S  virtio_blk.c    zram
aoe        cryptoloop.c  loop.h    null_blk.h        ps3disk.c    skd_main.c   swim.c      xen-blkback
ataflop.c  drbd          loop.o    null_blk_main.c   ps3vram.c    skd_s1120.h  sx8.c       xen-blkfront.c
brd.c      floppy.c      Makefile  null_blk_zoned.c  rbd.c        sunvdc.c     umem.c      xsysace.c
brd.o      Kconfig       mtip32xx  paride            rbd_types.h  swim3.c      umem.h      z2ram.c

这里我们以块设备驱动z2ram为例进行讲解分析。z2ram驱动是实用RAM模拟一段块设备,也就是ramdisk。

一、全局变量

z2ram块设备驱动的实现位于drivers/block/z2ram.c,在文件的开始定义了全局变量:

#define DEVICE_NAME "Z2RAM"

#include <linux/major.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/blk-mq.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/slab.h>

#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/pgtable.h>

#include <linux/zorro.h>


#define Z2MINOR_COMBINED      (0)
#define Z2MINOR_Z2ONLY        (1)
#define Z2MINOR_CHIPONLY      (2)
#define Z2MINOR_MEMLIST1      (4)
#define Z2MINOR_MEMLIST2      (5)
#define Z2MINOR_MEMLIST3      (6)
#define Z2MINOR_MEMLIST4      (7)
#define Z2MINOR_COUNT         (8) /* Move this down when adding a new minor */

#define Z2RAM_CHUNK1024       ( Z2RAM_CHUNKSIZE >> 10 )

static DEFINE_MUTEX(z2ram_mutex);
static u_long *z2ram_map    = NULL;
static u_long z2ram_size    = 0;
static int z2_count         = 0;
static int chip_count       = 0;
static int list_count       = 0;
static int current_device   = -1;

static DEFINE_SPINLOCK(z2ram_lock);

static struct gendisk *z2ram_gendisk;

static struct request_queue *z2_queue;
static struct blk_mq_tag_set tag_set;

static const struct blk_mq_ops z2_mq_ops = {
        .queue_rq       = z2_queue_rq,
};

二、块设备操作集

在drivers.block/z2ram.c文件中定义了块设备的操作:

static const struct block_device_operations z2_fops =
{
        .owner          = THIS_MODULE,
        .open           = z2_open,
        .release        = z2_release,
};

即打开块设备的的时候回调z2_open函数,关闭块设备的时候回调z2_release函数。

2.1 z2_open

static int z2_open(struct block_device *bdev, fmode_t mode)
{
    int device;
    int max_z2_map = ( Z2RAM_SIZE / Z2RAM_CHUNKSIZE ) *
        sizeof( z2ram_map[0] );
    int max_chip_map = ( amiga_chip_size / Z2RAM_CHUNKSIZE ) *
        sizeof( z2ram_map[0] );
    int rc = -ENOMEM;

    device = MINOR(bdev->bd_dev);

    mutex_lock(&z2ram_mutex);
    if ( current_device != -1 && current_device != device )
    {
        rc = -EBUSY;
        goto err_out;
    }

    if ( current_device == -1 )
    {
        z2_count   = 0;
        chip_count = 0;
        list_count = 0;
        z2ram_size = 0;

        /* Use a specific list entry. */
        if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) {
                int index = device - Z2MINOR_MEMLIST1 + 1;
                unsigned long size, paddr, vaddr;

                if (index >= m68k_realnum_memory) {
                        printk( KERN_ERR DEVICE_NAME
                                ": no such entry in z2ram_map\n" );
                        goto err_out;
                }

                paddr = m68k_memory[index].addr;
                size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE-1);

#ifdef __powerpc__
                /* FIXME: ioremap doesn't build correct memory tables. */
                {
                        vfree(vmalloc (size));
                }

                vaddr = (unsigned long)ioremap_wt(paddr, size);

#else
                vaddr = (unsigned long)z_remap_nocache_nonser(paddr, size);
#endif
                z2ram_map =
                        kmalloc_array(size / Z2RAM_CHUNKSIZE,
                                      sizeof(z2ram_map[0]),
                                      GFP_KERNEL);
                if ( z2ram_map == NULL )
                {
                    printk( KERN_ERR DEVICE_NAME
                        ": cannot get mem for z2ram_map\n" );
                    goto err_out;
                }

                while (size) {
                        z2ram_map[ z2ram_size++ ] = vaddr;
                        size -= Z2RAM_CHUNKSIZE;
                        vaddr += Z2RAM_CHUNKSIZE;
                        list_count++;
                }
                if ( z2ram_size != 0 )
                    printk( KERN_INFO DEVICE_NAME
                        ": using %iK List Entry %d Memory\n",
                        list_count * Z2RAM_CHUNK1024, index );
        } else

        switch ( device )
        {
            case Z2MINOR_COMBINED:

                z2ram_map = kmalloc( max_z2_map + max_chip_map, GFP_KERNEL );
                if ( z2ram_map == NULL )
                {
                    printk( KERN_ERR DEVICE_NAME
                        ": cannot get mem for z2ram_map\n" );
                    goto err_out;
                }

                get_z2ram();
                get_chipram();

                if ( z2ram_size != 0 )
                    printk( KERN_INFO DEVICE_NAME
                        ": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n",
                        z2_count * Z2RAM_CHUNK1024,
                        chip_count * Z2RAM_CHUNK1024,
                        ( z2_count + chip_count ) * Z2RAM_CHUNK1024 );

            break;

            case Z2MINOR_Z2ONLY:
                z2ram_map = kmalloc( max_z2_map, GFP_KERNEL );
                if ( z2ram_map == NULL )
                {
                    printk( KERN_ERR DEVICE_NAME
                        ": cannot get mem for z2ram_map\n" );
                    goto err_out;
                }

                get_z2ram();

                if ( z2ram_size != 0 )
                    printk( KERN_INFO DEVICE_NAME
                        ": using %iK of Zorro II RAM\n",
                        z2_count * Z2RAM_CHUNK1024 );

            break;

            case Z2MINOR_CHIPONLY:
                z2ram_map = kmalloc( max_chip_map, GFP_KERNEL );
                if ( z2ram_map == NULL )
                {
                    printk( KERN_ERR DEVICE_NAME
                        ": cannot get mem for z2ram_map\n" );
                    goto err_out;
                }

                get_chipram();

                if ( z2ram_size != 0 )
                    printk( KERN_INFO DEVICE_NAME
                        ": using %iK Chip RAM\n",
                        chip_count * Z2RAM_CHUNK1024 );

            break;

            default:
                rc = -ENODEV;
                goto err_out;

            break;
        }

        if ( z2ram_size == 0 )
        {
            printk( KERN_NOTICE DEVICE_NAME
                ": no unused ZII/Chip RAM found\n" );
            goto err_out_kfree;
        }

        current_device = device;
        z2ram_size <<= Z2RAM_CHUNKSHIFT;
        set_capacity(z2ram_gendisk, z2ram_size >> 9);
    }

    mutex_unlock(&z2ram_mutex);
    return 0;

err_out_kfree:
    kfree(z2ram_map);
err_out:
    mutex_unlock(&z2ram_mutex);
    return rc;
}
2.1.1 get_z2ram
static void
get_z2ram( void )
{
    int i;

    for ( i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++ )
    {
        if ( test_bit( i, zorro_unused_z2ram ) )
        {
            z2_count++;
            z2ram_map[z2ram_size++] = (unsigned long)ZTWO_VADDR(Z2RAM_START) +
                                      (i << Z2RAM_CHUNKSHIFT);
            clear_bit( i, zorro_unused_z2ram );
        }
    }

    return;
}
2.1.2 get_chipram
static void
get_chipram( void )
{

    while ( amiga_chip_avail() > ( Z2RAM_CHUNKSIZE * 4 ) )
    {
        chip_count++;
        z2ram_map[ z2ram_size ] =
            (u_long)amiga_chip_alloc( Z2RAM_CHUNKSIZE, "z2ram" );

        if ( z2ram_map[ z2ram_size ] == 0 )
        {
            break;
        }

        z2ram_size++;
    }

    return;
}

2.2 z2_release

static void
z2_release(struct gendisk *disk, fmode_t mode)
{
    mutex_lock(&z2ram_mutex);
    if ( current_device == -1 ) {
        mutex_unlock(&z2ram_mutex);
        return;
    }
    mutex_unlock(&z2ram_mutex);
    /*
     * FIXME: unmap memory
     */
}

三、模块入口函数

module_init(z2_init);

我们定位到模块入口函数z2_init:

static int __init
z2_init(void)
{
    int ret;

    if (!MACH_IS_AMIGA)
        return -ENODEV;

    ret = -EBUSY;
    if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME))  
        goto err;

    ret = -ENOMEM;
    z2ram_gendisk = alloc_disk(1);
    if (!z2ram_gendisk)
        goto out_disk;

    z2_queue = blk_mq_init_sq_queue(&tag_set, &z2_mq_ops, 16,
                                        BLK_MQ_F_SHOULD_MERGE);
    if (IS_ERR(z2_queue)) {
        ret = PTR_ERR(z2_queue);
        z2_queue = NULL;
        goto out_queue;
    }

    z2ram_gendisk->major = Z2RAM_MAJOR;
    z2ram_gendisk->first_minor = 0;
    z2ram_gendisk->fops = &z2_fops;
    sprintf(z2ram_gendisk->disk_name, "z2ram");

    z2ram_gendisk->queue = z2_queue;
    add_disk(z2ram_gendisk);
    blk_register_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT, THIS_MODULE,
                                z2_find, NULL, NULL);

    return 0;

out_queue:
    put_disk(z2ram_gendisk);
out_disk:
    unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME);
err:
    return ret;
}

在模块的入口函数中,主要执行了如下操作:

  • 调用register_blkdev注册块设备主设备号,这里主设备号定义为Z2RAM_MAJOR=37,块设备名称为DEVICE_NAME="Z2RAM";
  • 使用alloc_disk申请一个通用磁盘对象gendisk,并且创建1个分区(除了代表磁盘的0号分区外);
  • 使用blk_mq_init_sq_queue初始化一个请求队列;
  • 设置gendisk结构体的成员:
    • 设置成员参数major、first_minor、disk_name、fops;
    • 设置请求队列queue,等于之前初始化的请求队列;
  • 使用add_disk注册gendisk;
  • 使用blk_register_region建立磁盘设备号和gendisk的映射关系(这一步是不是多余的);

3.1 z2_find

static struct kobject *z2_find(dev_t dev, int *part, void *data)
{
        *part = 0;
        return get_disk_and_module(z2ram_gendisk);
}

3.2 z2_mq_ops

z2_mq_ops为为块设备驱动行为的回调函数,z2_mq_ops定义为:

static const struct blk_mq_ops z2_mq_ops = {
        .queue_rq       = z2_queue_rq,
};

一旦I/O请求就绪,并且I/O调度器不想再把请求保持在队列上来排序或扩展,I/O调度器就会调用queue_rq函数。

z2_queue_rq函数定义为:

static blk_status_t z2_queue_rq(struct blk_mq_hw_ctx *hctx,
                                const struct blk_mq_queue_data *bd)
{
        struct request *req = bd->rq;
        unsigned long start = blk_rq_pos(req) << 9;
        unsigned long len  = blk_rq_cur_bytes(req);

        blk_mq_start_request(req);

        if (start + len > z2ram_size) {
                pr_err(DEVICE_NAME ": bad access: block=%llu, "
                       "count=%u\n",
                       (unsigned long long)blk_rq_pos(req),
                       blk_rq_cur_sectors(req));
                return BLK_STS_IOERR;
        }

        spin_lock_irq(&z2ram_lock);

        while (len) {
                unsigned long addr = start & Z2RAM_CHUNKMASK;
                unsigned long size = Z2RAM_CHUNKSIZE - addr;
                void *buffer = bio_data(req->bio);

                if (len < size)
                        size = len;
                addr += z2ram_map[ start >> Z2RAM_CHUNKSHIFT ];
                if (rq_data_dir(req) == READ)
                        memcpy(buffer, (char *)addr, size);
                else
                        memcpy((char *)addr, buffer, size);
                start += size;
                len -= size;
        }

        spin_unlock_irq(&z2ram_lock);
        blk_mq_end_request(req, BLK_STS_OK);
        return BLK_STS_OK;
}

四、模块出口函数

module_exit(z2_exit);

我们定位到模块入口函数z2_exit:

static void __exit z2_exit(void)
{
    int i, j;
    blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT);
    unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME);
    del_gendisk(z2ram_gendisk);
    put_disk(z2ram_gendisk);
    blk_cleanup_queue(z2_queue);
    blk_mq_free_tag_set(&tag_set);

    if ( current_device != -1 )
    {
        i = 0;

        for ( j = 0 ; j < z2_count; j++ )
        {
            set_bit( i++, zorro_unused_z2ram );
        }

        for ( j = 0 ; j < chip_count; j++ )
        {
            if ( z2ram_map[ i ] )
            {
                amiga_chip_free( (void *) z2ram_map[ i++ ] );
            }
        }

        if ( z2ram_map != NULL )
        {
            kfree( z2ram_map );
        }
    }

    return;
}

在模块的出口函数中,主要执行了如下操作:

  • 调用blk_unregister_region取消磁盘设备号和gendisk的映射关系(这一步是不是多余的);
  • 调用unregister_blkdev取消块设备主设备号注册;
  • 调用del__gendisk释放磁盘gendisk;
  • 调用put_disk注销磁盘gendisk;
  • 调用blk_cleanup_queue清除内核中的请求队列request_queue;
  • 调用blk_mq_free_tag_set释放 blk_mq_alloc_tag_set申请的标签集tag_set;

参考文章

[1]22.Linux-块设备驱动之框架详细分析(详解)

[2]23.Linux-块设备驱动(详解)

标签:map,Z2RAM,queue,z2ram,linux,驱动,z2,size
From: https://www.cnblogs.com/zyly/p/16704365.html

相关文章

  • 《Unix/Linux系统编程》学习笔记4
    第七章文件操作一.知识点归纳(一)文件操作级别文件操作分为五个级别,按照从低到高的顺序排列如下:1.硬件级别:硬件级别的文件操作包括:fdisk:将硬盘、U盘或SDC盘分区。mk......
  • Linux 网卡实时流量查看脚本
    Linux网卡实时流量查看脚本#!/bin/bashPATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin;exportPATHfunctiontraffic_monitor{OS_NAME=$......
  • dapr-安装之-linux版
    1.安装包准备:下载地址:https://github.com/dapr/installer-bundle/releases2.服务器准备准备centos7一台3.前置环境docker4.下载安装包由于我们计划......
  • 《Unix&Linux系统编程》第七章学习笔记
    第7章文件操作7.1文件操作级别1.硬件级别fdisk:将硬盘、U盘或SDC盘分区mkfs:格式化磁盘分区,为系统做好准备fsck:检查和维修系统碎片整理:压缩文件系统中的文件2.操......
  • 《Unix/Linux系统编程》第七、八章学习笔记
    第七章文件操作7.1文件操作级别文件操作分为五个级别,按照从高到低的顺序排列如下:(1)硬件级别fdisk:将硬盘、U盘或SDC盘分区。mkfs:格式化磁盘分区,为系统做好准备。f......
  • Linux pssh安装与使用
    Linuxpssh安装与使用说明:我这是没有在密钥认证的情况下操作1、安装pssh[root@libinansible]#yuminstall-ypssh[root@libinansible]#rpm-qlpssh/usr/bin/pnuk......
  • 弃用 Windows,政府机构 5000 万台电脑将替换为国产 Linux!
    来源:https://www.linuxmi.com/50-million-pc-linux.html开源社区的一大胜利!继德国之后,中国现在想在5000万台PC上抛弃Windows并运行Linux!如果您一直密切关注Lin......
  • Linux错题集1
    错题1在Linux系统中,小王希望将他执行ls命令的输出结果保存在当前目录下的文件output.ls中,以供日后进行分析和使用,但要求不覆盖原文件的内容,他应该使用的命令是A.ls>outpu......
  • 在Linux下的文件IO的使用(一)
    系统调用系统调用:操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务为什么用户程序不能直接访问系......
  • Linux学习(一)从搭建自己的code-server开始
    前言某天碰巧看到了网页版的VSCode,发现居然有这种好东西,浏览器访问https://vscode.dev/,ipad终于能当生产力工具而不是爱奇艺播放器了o.O其实这个东西已经够用了,代码......