免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4530 | 回复: 16
打印 上一主题 下一主题

[内核入门] make_request_fn 中能否调用wait_for_completion等bio完成. [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-11-27 15:53 |只看该作者 |倒序浏览
本帖最后由 mfkp3 于 2014-11-28 11:02 编辑

本人内核刚入门,尝试写个简单块设备模块例子。遇到一些问题,诚向大家请教:

例子中虚拟一个块设备minidev, 底层的实际裸设备是/dev/sda. 实现make_request函数,在make_request函数中封装下发的bio, 并等待该bio完成,以实现同步写。但是发现系统并没有触发我的end_io, 导致写的进程卡死。
代码片段如下:
--------------------我贴上全部代码,麻烦大家帮忙分析下。------------------ miniblk.tar.gz (1.83 KB, 下载次数: 31)
-----------------miniblk.h----------------
#ifndef __MINIBLK__
#define __MINIBLK__

#include <linux/blkdev.h>

#define MINIDEV_NAME            "minibd"
/* 修改成相应的磁盘名,危险操作 */
#define MINIDEV_BDEV            "/dev/sda"

struct minidev {
        struct task_struct *task;
        struct gendisk *disk;
        struct request_queue *rq;
        dev_t major;
        struct block_device *bdev;
};

#endif // __MINIBD_H__

-----------------miniblk.c----------------
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/buffer_head.h>
#include <linux/kthread.h>
#include <linux/bio.h>
#include "miniblk.h"

struct minidev *minidev;
static DECLARE_COMPLETION(bio_finish);

static struct block_device_operations minidev_fops = {
        .owner = THIS_MODULE,
};

struct mybio {
        int err;
        struct bio *bio;
        void *private;
        bio_end_io_t *end_io;
};

struct mybio *init_mybio(struct bio *bio)
{
        struct mybio *mybio = kzalloc(sizeof(struct mybio), GFP_KERNEL);
        if (!mybio)
                return NULL;
        mybio->bio = bio;
        mybio->private = bio->bi_private;
        mybio->end_io = bio->bi_end_io;

        return mybio;
}

void free_mybio(struct mybio *mybio)
{
        kfree(mybio);
}

void mybio_endio(struct bio *bio, int err)
{
        struct mybio *mybio = bio->bi_private;
        //complete(&bio_finish);

        bio->bi_private = mybio->private;
        bio->bi_end_io = mybio->end_io;
        if (bio->bi_end_io)
                bio->bi_end_io(bio, err);
        free_mybio(mybio);
}

static void minidev_make_request(struct request_queue *q, struct bio *bio)
{
        static uint64_t count = 0;
        struct mybio *mybio;

        mybio = init_mybio(bio);
        if (mybio == NULL) {
                bio_endio(bio, -EIO);
                pr_info("wrapper init failed.\n");
        }

        bio->bi_private = mybio;
        bio->bi_end_io = mybio_endio;
        bio->bi_bdev = minidev->bdev;
        submit_bio(bio->bi_rw & WRITE, bio);

        pr_info("submit bio %p.\n", bio);
        //wait_for_completion(&bio_finish);
        pr_info("make request handler count: %llu.bio:%p\n", ++count, bio);
        return;
}

static int init_minidev(struct minidev *dev)
{
        int ret = 0;
        struct request_queue *q;
        struct gendisk *d;
        struct block_device *bdev;
        dev_t major;

        major = register_blkdev(0, MINIDEV_NAME);
        if(major < 0) {
                return major;
        }
        dev->major = major;

        q = blk_alloc_queue(GFP_KERNEL);
        if(!q) {
                ret = -ENOMEM;
                goto err_queue;
        }
        blk_queue_make_request(q, minidev_make_request);
        dev->rq = q;

        d = alloc_disk(1);
        if(!d) {
                ret = -ENOMEM;
                goto err_disk;
        }
        strncpy(d->disk_name, MINIDEV_NAME, strlen(MINIDEV_NAME));
        d->major = major;
        d->first_minor = 0;
        d->fops = &minidev_fops;
        d->queue = q;
        dev->disk = d;

        bdev = blkdev_get_by_path(MINIDEV_BDEV, FMODE_READ | FMODE_WRITE, dev);
        if(IS_ERR(bdev)) {
                printk(KERN_INFO "open bdev fail!");
                ret = -EBUSY;
                goto err_bdev;
        }
        set_capacity(dev->disk, i_size_read(bdev->bd_inode) >> 9);
        dev->bdev = bdev;
        add_disk(minidev->disk);

        return 0;

err_bdev:
        put_disk(minidev->disk);

err_disk:
        blk_cleanup_queue(q);

err_queue:
        unregister_blkdev(major, MINIDEV_NAME);
        return ret;
}

static int __init minidev_init(void)
{
        int ret = 0;
        minidev = kzalloc(sizeof(struct minidev), GFP_KERNEL);
        if(!minidev) {
                ret = -ENOMEM;
                goto fail;
        }

        pr_info("ready init minidev.\n");
        ret = init_minidev(minidev);
        if(ret) {
                goto fail;
        }

        printk(KERN_INFO "miniblk init!\n");
        return 0;

fail:
        kfree(minidev);
        return ret;
}

static void __exit minidev_exit(void)
{
        del_gendisk(minidev->disk);
        put_disk(minidev->disk);
        blkdev_put(minidev->bdev, FMODE_READ | FMODE_WRITE);
        blk_cleanup_queue(minidev->rq);
        unregister_blkdev(minidev->major, MINIDEV_NAME);
        kfree(minidev);

        printk(KERN_INFO "miniblk exit!\n");
}

module_init(minidev_init);
module_exit(minidev_exit);
MODULE_LICENSE("GPL");

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
2 [报告]
发表于 2014-11-27 20:42 |只看该作者
回复 1# mfkp3

为什么虚拟块设备会绕过来去访问真实的/dev/sda的呢。建议初学的话以《Linux设备驱动》的例子来学习。
同时,非常推荐你看一下 飞翔的鸵鸟  的经典之作 《写一个块设备驱动》
http://bbs.chinaunix.net/thread-2017377-1-1.html
   

论坛徽章:
2
寅虎
日期:2014-11-25 21:47:342015小元宵徽章
日期:2015-03-06 15:58:18
3 [报告]
发表于 2014-11-27 20:43 |只看该作者
有没有完整的代码?建议在struct mybio {
        struct bio *bio;
        void *private;
        bio_end_io_t *end_io;
        int err;
};增加struct completion event;成员,不要用static DECLARE_COMPLETION(bio_finish);这种的。
你可以参考内核的实现如下:
  1. static void bi_complete(struct bio *bio, int error) {
  2.          complete((struct completion*)bio->bi_private);
  3. }
  4. int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct page *page, int rw) {
  5.          struct bio *bio = bio_alloc(GFP_NOIO, 1);
  6.          struct completion event;  int ret;
  7.          rw |= (1 << BIO_RW_SYNC);
  8.          bio->bi_bdev = bdev;
  9.          bio->bi_sector = sector;
  10.          bio_add_page(bio, page, size, 0);
  11.          init_completion(&event);
  12.          bio->bi_private = &event;
  13.          bio->bi_end_io = bi_complete;
  14.          submit_bio(rw, bio);
  15.          wait_for_completion(&event);
  16.          ret = test_bit(BIO_UPTODATE, &bio->bi_flags);
  17.          bio_put(bio);
  18.          return ret;
  19. }EXPORT_SYMBOL_GPL(sync_page_io);
复制代码

论坛徽章:
2
寅虎
日期:2014-11-25 21:47:342015小元宵徽章
日期:2015-03-06 15:58:18
4 [报告]
发表于 2014-11-27 20:50 |只看该作者
如果想实现把一个物理scsi块设备虚拟成一个 virtual target,可以看看md的实现。
你的代码不全,无法确定minidev_make_request函数是否被调用,它的注册方式等?

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
5 [报告]
发表于 2014-11-28 08:59 |只看该作者
感觉好像逻辑有问题?make_request-->submit_bio-->make_request-->submit_bio-->make_request......

论坛徽章:
0
6 [报告]
发表于 2014-11-28 09:25 |只看该作者
回复 2# Tinnal

谢谢你的建议,我只是不明白为什么这里会出现问题,探索本身也是一种学习~


   

论坛徽章:
0
7 [报告]
发表于 2014-11-28 09:33 |只看该作者
回复 4# 镇水铁牛


   谢谢你的回复,请教下为什么放在struct里比定义静态的要好呢? 在这里我只是为了处理方便。
   另外,我有在minidev_make_request中打印log, 我能清楚的看到log打出来了, 并且内核发出警告也指明进程在wait_for_completion中阻塞时间过长,所以才奇怪明明下发下去了,应该是加入到实际bdev的request_queue,   为什么没有endio调用,
   
   我再仔细整理下我的代码,看是否哪个地方有疏漏。

论坛徽章:
0
8 [报告]
发表于 2014-11-28 09:37 |只看该作者
回复 5# humjb_1983

我的minidev_make_request中改变了bio的bdev, 因此下发下去后调用generic_make_request时, request_queue变了, make_request_fn也变了,应该不会循环吧。
   

论坛徽章:
2
寅虎
日期:2014-11-25 21:47:342015小元宵徽章
日期:2015-03-06 15:58:18
9 [报告]
发表于 2014-11-28 20:22 |只看该作者
看了你的代码,应该没啥大问题,修改下这里试试看:
d->first_minor = 0;
d->minors = 16;
但是这个gendisk的minors域你要设置下,不设置应该是初始值0了,设置范围是1~16

论坛徽章:
0
10 [报告]
发表于 2014-11-29 11:11 |只看该作者
回复 9# 镇水铁牛

代码如果注释掉那两行completion,就一切正常,进程也不会卡死,可是这是为什么呢?
   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP