2013-09-10 2 views
1

Я создаю очень простой блок RAM-диска на основе sbull.Невозможно прочитать/записать диск с блоком ram со смещением

Пока он работает нормально, если я читаю/записываю блоки данных с помощью dd, но всякий раз, когда я пытаюсь установить на него файловую систему (а иногда и создавать файловую систему), мой драйвер падает.

После долгих недель отладки я наконец выяснил, что не так, хотя я не могу найти способ решить проблему. Отсюда мой вопрос здесь:

Всякий раз, когда приложение пользовательского пространства создает запрос к устройству С СМЕЩЕНИЕМ, драйвер не работает! Позвольте мне показать вам исходный код для того, чтобы выяснить:

Прежде всего, я обработки запросов с использованием mk_request (не используя request_queue):

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 функция:

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, переменная «off» в esd_do_bvec() всегда равна 0, независимо от того, где и сколько байтов я хочу записать. Файловая система, очевидно, всегда выполняет ввод-вывод в блоках 4 КБ и будет писать полный блок, даже если требуется заменить только один байт.

Я уверен, что чтение и запись корректно работают, когда нет смещения, потому что я создал файл размером с мой RAM-диск и выгрузил весь файл на свое устройство, используя dd, а затем получил вывод (также используя dd), а входные и выходные файлы - это то же самое. Я также написал тот же файл в brd (исходный блок ядра RAM RAM-диска), а выходы совпадают с моим устройством и устройством Brd.

BUT - В некоторых конкретных ситуациях я пытаюсь установить или создать новую файловую систему на своем устройстве, и как-то она получает запросы ввода-вывода со смещением, и в этот момент мой драйвер выходит из строя. Я предполагаю, что я неправильно обрабатываю смещение. Например, при попытке "установить -t ext2/DEV/ESDA":

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 

(PS: вывод показывает "EXT4", но я бегу с "-t ext2")

У меня есть проверил содержимое сектора n. 2 в моем устройстве, и он содержит метаданные ext2 (так как я запускал mkfs.ext2, прежде чем пытаться смонтировать, конечно). Поэтому я считаю, что есть проблема со смещением. Пока я не могу отлаживать свой драйвер, потому что не смог найти запрос, который вызовет запрос ввода-вывода со смещением (например, если я попытаюсь записать один байт в свое устройство, то Linux будет читать весь блок и переписать его только с одним другим байтом).

Надеюсь, это не слишком простой вопрос для вас.

Спасибо заранее, Фил




Пожалуйста, смотрите ответ предоставленный Peter ниже.

Если вам интересно, что функция esd_do_bvec() выглядит сейчас, здесь речь идет о:

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; 
} 
+0

Является ли 'data' в' struct escsi_dev' указателем char? На первый взгляд, похоже, что это может быть '_u32 *', что, конечно, объясняло бы проблемы смещения. –

+0

Привет, это u8 *. – Phil

ответ

3

Смещения в сегмент не относится к смещению от места блочного устройства, а смещение на страницу. Чтобы это было отличным от нуля, вам, вероятно, потребуется написать свою собственную программу на C, которая запускает read() и write(). Выделите буфер с выравниванием по страницам, затем прочитайте/напишите в/из разных мест в этом буфере, и они должны отображаться как смещения в bvec.

Это говорит, LWN предупреждает управление этой страницей смещения вручную, и рекомендует вместо макроса bio_kmap_irq(), который называется на bio_for_each_segment() переменной bio, и заботится о атомном KMAP и управляет записью смещения, а также. Источник: http://lwn.net/Articles/26404/

Ваш код будет выглядеть примерно так:

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 принять буфер памяти напрямую, а не страница/смещения.

+0

спасибо большое, Питер работал! Я смутился насчет смещения, вы были правы. Просто наблюдение, я верю, что вы сказали: bio_kunmap_irq (bio, & flags); Вы имели в виду: bio_kunmap_irq (buf, & flags); правый? – Phil

+0

Да, конечно! Фиксация ответа ... – Peter

Смежные вопросы