首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >块内存磁盘无法读取/写入偏移量

块内存磁盘无法读取/写入偏移量
EN

Stack Overflow用户
提问于 2013-09-10 22:00:54
回答 1查看 992关注 0票数 2

我正在创建一个非常简单的基于sbull的块RAM磁盘。

到目前为止,如果我使用dd读取/写入数据块,它可以正常工作,但是每当我尝试在其上挂载文件系统(有时还创建一个文件系统)时,我的驱动程序就会崩溃。

经过长时间的调试,我终于发现了问题所在,尽管我无法真正找到解决问题的方法。因此,我在此提出质询:)

每当用户空间应用程序使用偏移量创建对设备的请求时,驱动程序将无法工作!让我向您展示源代码,以便澄清:

首先,我使用mk_request (而不是使用request_queue)处理请求:

代码语言:javascript
复制
static void escsi_mk_request(struct request_queue *q, struct bio *bio)
{
        struct block_device *bdev = bio->bi_bdev;
        struct escsi_dev *esd = bdev->bd_disk->private_data;
        int rw;
        struct bio_vec *bvec;
        sector_t sector;
        int i;
        int err = -EIO;

        printk("request received nr. sectors = %lu\n",bio_sectors(bio));

        sector = bio->bi_sector;
        if (bio_end_sector(bio) > get_capacity(bdev->bd_disk))
                goto out;

        if (unlikely(bio->bi_rw & REQ_DISCARD)) {
                err = 0;
                goto out;
        }

        rw = bio_rw(bio);
        if (rw == READA)
            rw = READ;

        bio_for_each_segment(bvec, bio, i) {
                unsigned int len = bvec->bv_len;
                err = esd_do_bvec(esd, bvec->bv_page, len, bvec->bv_offset, rw, sector);
                if (err) {
                        printk("err!\n");
                        break;
                }
                sector += len >> SECTOR_SHIFT;
        }

out:
        bio_endio(bio, err);
}

esd_do_bvec函数:

代码语言:javascript
复制
static int esd_do_bvec(struct escsi_dev *esd, struct page *page,
                         unsigned int len, unsigned int off, int rw,
                         sector_t sector)
 {
            void *mem;
            int err = 0;
            unsigned int offset;
            int i;

        offset = off + sector * 512;

        printk("ESD RW=%d, len=%d, off=%d, offset=%d, sector=%lu\n",rw,len,off,offset,sector);

        mem = kmap_atomic(page);
        if (rw == READ) {
                memcpy(mem,esd->data+offset,len);
        } else {
                memcpy(esd->data+offset,mem,len);
        }
        kunmap_atomic(mem);

out:
        return err;
}

好的,所以基本上当我使用dd读写数据时,esd_do_bvec()中的变量"off“始终是0,不管我想在哪里写多少字节。文件系统显然总是以4KB块执行I/O,即使只需要替换一个字节,也会编写完整的块。

我确信在没有偏移量的情况下读写是正确的,因为我创建了一个与我的块RAM磁盘大小相同的文件,并使用dd将整个文件转储到我的设备中,然后得到设备的输出(也使用dd),并且输入和输出文件完全相同。我还将相同的文件写入brd (Linux内核原始块RAM磁盘驱动程序),输出结果与我的设备和brd设备相同。

但是--在某些特定的情况下,我尝试在我的设备上挂载或创建一个新的文件系统,它以某种方式获得带有偏移量的I/O请求,这时我的驱动程序就失败了。我认为我没有正确地处理偏移量。例如,当我尝试“挂载-t ext2 /dev/esda”时:

代码语言:javascript
复制
linux-xjwl:/home/phil/escsi # mount /dev/esda -t ext2 /mnt/esda1/
mount: wrong fs type, bad option, bad superblock on /dev/esda,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail or so
linux-xjwl:/home/phil/escsi # dmesg|tail -n 10
[ 2239.275901] ESD RW=0, len=4096, off=0, offset=16384, sector=32
[ 2239.275947] request received nr. sectors = 8
[ 2239.275959] ESD RW=0, len=4096, off=0, offset=4096, sector=8
[ 2239.276516] request received nr. sectors = 8
[ 2239.276537] ESD RW=0, len=4096, off=0, offset=2097152, sector=4096
[ 2239.276606] request received nr. sectors = 8
[ 2239.276626] ESD RW=0, len=4096, off=0, offset=28672, sector=56
[ 2239.277535] request received nr. sectors = 2
[ 2239.277535] ESD RW=0, len=1024, off=1024, offset=2048, sector=2
[ 2239.277535] EXT4-fs (esda): VFS: Can't find ext4 filesystem

(p.s.:输出显示"EXT4“,但我运行的是"-t ext2")

我已经检查了设备中n.2扇区的内容,并且它确实包含ext2元数据(当然,因为我在尝试挂载之前运行了mkfs.ext2 )。所以我相信偏移有问题。到目前为止,我无法真正调试我的驱动程序,因为我无法提出一个请求,这将导致一个带有偏移量的I/O请求(例如,如果我尝试将一个字节写入我的设备,Linux将读取整个块并用一个不同的字节重写它)。

希望这对你来说不是个太简单的问题。

谢谢你,菲尔

请看下面彼得提供的答案。

如果您想知道esd_do_bvec()函数现在是什么样子,那么它就来了:

代码语言:javascript
复制
static int esd_do_bvec(struct escsi_dev *esd, char *buf,
                        unsigned int len, int rw, sector_t sector)
{
        int err = 0;
        unsigned int offset;

        // Please notice that we STILL have an offset to deal with, but
        // this offset comes in sectors and needs to be converted to a
        // a byte offset.
        offset = sector << SECTOR_SHIFT; // or multiply by 512

        //printk("ESD RW=%d, len=%d, off=%d, offset=%d, sector=%lu\n",rw,len,off,offset,sector);

        if (rw == READ) {
                memcpy(buf,esd->data+offset,len);
        } else {
                memcpy(esd->data+offset,buf,len);
        }
        return err;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-09-11 14:17:35

每个段的偏移量并不是指来自块设备位置的偏移量,而是指向页面的偏移量。要使其成为非零,您可能需要编写运行read()write()的自己的C程序。分配一个与页面对齐的缓冲区,然后从该缓冲区中的不同位置读取/写入缓冲区,这些缓冲区应该在bvec中显示为偏移。

尽管如此,LWN提醒您手动管理此页面偏移量,并建议使用宏bio_kmap_irq() ( bio_for_each_segment()变量bio上调用的宏),负责原子kmap并管理偏移项。来源:http://lwn.net/Articles/26404/

您的代码将类似于:

代码语言:javascript
复制
    bio_for_each_segment(bvec, bio, i) {
            unsigned int len = bvec->bv_len;
            unsigned long flags;

            char *buf = bio_kmap_irq(bio, &flags);
            err = esd_do_bvec(esd, buf, len, rw, sector);
            bio_kunmap_irq(buf, &flags);

            if (err) {
                    printk("err!\n");
                    break;
            }
            sector += len >> SECTOR_SHIFT;
    }

当然,这会将esd_do_bvec的签名更改为直接接受内存缓冲区,而不是页面/偏移量。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18729466

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档