The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]




Версия для распечатки Пред. тема | След. тема
Новые ответы [ Отслеживать ]
Фильтр block device в Линукс, !*! DoubleHead, 26-Июн-15, 15:34  [смотреть все]
Всем доброго времени суток.

Изучаю потихоньку разработку модулей ядра. И пытаюсь написать простенький фильтр для блочного устройства.

Если я все правильно понимаю, теоретически то на каждую операцию записи и чтения должны вызываться функция make_request_fn, которую я собственно перехватываю. Проблема в том, что я вижу вызовы этой функции на запись, и изредка на чтение. Такое ощущение, что данные либо где-то кэшируются, либо используется еще какой-то механизм..

Собственно вопрос в том, почему так происходит?

Вот мой код:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#define PARTITION_NAME "/dev/sdb"


char *ptr;
int i;
struct block_device *bd;
make_request_fn *orig_mrqfn;

static void filter_mrqfn(struct request_queue *rq, struct bio *b)
{
    printk(KERN_INFO "filter: Sector=lu Size=%d bi_rw=%lu\n", b->bi_sector, b->bi_size, b->bi_rw);

    ptr=(char *)bio_data(b);
    for(i=0;i<b->bi_size;i++)  //4096 as bio is going to be in 4kb chunk?????
    {
       printk("%c",*ptr);
       ptr++;
    }


    orig_mrqfn(rq, b); /* calling the original make_request_fn() */
}

/*
* set_mrqfn() - Change the original make_request_fn() to our
* modules request function
*/
static void set_mrqfn(void)
{
    struct super_block *sb;
    printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
    /* lock filesystem to prevent any further changes */
    fsync_bdev(bd);
    sb = freeze_bdev(bd);
    if (bd->bd_disk->queue->make_request_fn == filter_mrqfn) {
        printk(KERN_INFO "filter: modules request function is already active\n");
    } else {
        /* save the pointer to the original make_request_fn() */
        orig_mrqfn = bd->bd_disk->queue->make_request_fn;
        /* replace the original with our modules request function */
        bd->bd_disk->queue->make_request_fn = filter_mrqfn;
    }
    
    /* unlock filesystem */
    thaw_bdev(bd, sb);
}

/*
* restore_mrqfn() - Restore the original make_request_fn()
*/
static void restore_mrqfn(void)
{
    struct super_block *sb = bd->bd_super;
    printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
    if (orig_mrqfn) {
        /* lock filesystem to prevent any further changes */
        fsync_bdev(bd);
        sb = freeze_bdev(bd);
        /* restore the original request function */
        bd->bd_disk->queue->make_request_fn = orig_mrqfn;
        /* unlock filesystem */
        thaw_bdev(bd, sb);
    }
    orig_mrqfn = NULL;
}

static int __init filter_init(void)
{
    printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
    orig_mrqfn = NULL;
    // Read block device from path
    bd = blkdev_get_by_path(PARTITION_NAME, FMODE_READ, NULL);
    if (IS_ERR(bd)) {
        printk(KERN_ERR "filter: failed to get block device\n");
        return 0;
    }
    
    printk(KERN_INFO "filter: found block device with major number %d\n", bd->bd_disk->major);
    printk(KERN_INFO "filter: file system block size %d\n", bd->bd_block_size);
    printk(KERN_INFO "filter: start sector %lu\n", (unsigned long)bd->bd_part->start_sect);
    printk(KERN_INFO "filter: number of sectors %lu\n", (unsigned long)bd->bd_part->nr_sects);
    printk(KERN_INFO "filter: logical block size %d\n", bdev_logical_block_size(bd));
    printk(KERN_INFO "filter: physical block size %d\n", bdev_physical_block_size(bd));
    set_mrqfn();
    return 0;
}

static void __exit filter_exit(void)
{
    printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
    restore_mrqfn();
    if (!IS_ERR(bd)) {
        blkdev_put(bd, FMODE_READ);
    }
}

module_init(filter_init);
module_exit(filter_exit);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Me");

  • Фильтр block device в Линукс, !*! Павел Самсонов, 20:13 , 26-Июн-15 (1)
    >[оверквотинг удален]
    >  printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
    >  restore_mrqfn();
    >  if (!IS_ERR(bd)) {
    >   blkdev_put(bd, FMODE_READ);
    >  }
    > }
    > module_init(filter_init);
    > module_exit(filter_exit);
    > MODULE_LICENSE("Dual MIT/GPL");
    > MODULE_AUTHOR("Me");

    Что у вас на /dev/sdb? Гадать не буду, такое поведение, значит так и есть. Может дисковый кэш сбрасывается. Можно посмотреть что при команде sync будет.

    • Фильтр block device в Линукс, !*! DoubleHead, 20:53 , 26-Июн-15 (3)
      > Что у вас на /dev/sdb? Гадать не буду, такое поведение, значит так
      > и есть. Может дисковый кэш сбрасывается. Можно посмотреть что при команде
      > sync будет.

      /dev/sdb это SCSI диск в виртуалке.

      При выполнении sync как и ожидается происходит запись буферизофанных данных и вызов make_request_fn().

      Поведение меня смущает только тем, что я не вижу операций чтения. На небольших файлах, ну скажем выполнение echo 1234 > /mnt/mydevice/1 логично вызывают несколько операций записи и соответственно вызовов перехваченной функции и я их вижу. А последующая cat /mnt/mydevice/1 отрабатывая совершенно нормально не влечет за собой вызовов моей функции, хотя должно бы.

  • Фильтр block device в Линукс, !*! Павел Самсонов, 20:31 , 26-Июн-15 (2)
    >[оверквотинг удален]
    >  printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
    >  restore_mrqfn();
    >  if (!IS_ERR(bd)) {
    >   blkdev_put(bd, FMODE_READ);
    >  }
    > }
    > module_init(filter_init);
    > module_exit(filter_exit);
    > MODULE_LICENSE("Dual MIT/GPL");
    > MODULE_AUTHOR("Me");

    Диск примонтирован? Может дисковый кэш. Что будет при команде sync?

    • Фильтр block device в Линукс, !*! DoubleHead, 20:57 , 26-Июн-15 (4)
      > Диск примонтирован? Может дисковый кэш. Что будет при команде sync?

      Без разницы смонтирован диск или нет, на устройстве может вообще не быть никакой ФС, так как вызов make_request_fn в данном случае лежит сразу над драйвером физического устройства.

      • Фильтр block device в Линукс, !*! SysManOne, 17:32 , 13-Ноя-18 (5)
        >> Диск примонтирован? Может дисковый кэш. Что будет при команде sync?
        > Без разницы смонтирован диск или нет, на устройстве может вообще не быть
        > никакой ФС, так как вызов make_request_fn в данном случае лежит сразу
        > над драйвером физического устройства.

        Удалось разобраться ?

        • Фильтр block device в Линукс, !*! Doublehead, 21:17 , 13-Ноя-18 (6)
          >>> Диск примонтирован? Может дисковый кэш. Что будет при команде sync?
          >> Без разницы смонтирован диск или нет, на устройстве может вообще не быть
          >> никакой ФС, так как вызов make_request_fn в данном случае лежит сразу
          >> над драйвером физического устройства.
          > Удалось разобраться ?

          Нет, увы. Забросил.
          А у вас тоже такая же проблема?

        • Фильтр block device в Линукс, !*! Doublehead, 21:44 , 13-Ноя-18 (7)
          >>> Диск примонтирован? Может дисковый кэш. Что будет при команде sync?
          >> Без разницы смонтирован диск или нет, на устройстве может вообще не быть
          >> никакой ФС, так как вызов make_request_fn в данном случае лежит сразу
          >> над драйвером физического устройства.
          > Удалось разобраться ?

          Сорри, поторопился я с ответом. Сейчас в код глянул, там вот такое. Насколько я помню, это работает и на запись и на чтение, но при большой нагрузке валит систему. Разбираться почему это происходит было, честно говоря уже лень. Если разберетесь, не забудьте поделиться :)


          #include <linux/init.h>
          #include <linux/module.h>
          #include <linux/fs.h>
          #include <linux/blkdev.h>
          #include <linux/buffer_head.h>
          #define PARTITION_NAME "/dev/sdb"


          char *ptr;
          int i;

          struct block_device *bd;

          make_request_fn *orig_mrqfn;

          bio_end_io_t *old_endio;

          // BIOs set
          struct bio_set * bs;

          // HOOKs
          static struct kmem_cache *my_hooks;

          // HOOK
          struct hook{
              void *old_private;
              bio_end_io_t *old_endio;
              struct bio *clone;
          };

          // read bi_end_io

          static void read_endio(struct bio *b, unsigned done, int error)
          {

              printk(KERN_INFO "filter: my bio_endio err=%d size=%u sector=%lu vi_vcnt=%d",error,done,b->bi_sector,b->bi_vcnt);
              struct hook *hook = b->bi_private;
              
              b->bi_end_io = hook->old_endio;
              b->bi_private = hook->old_private;
              

              unsigned int count=0;

              printk(KERN_INFO " clone_size=%u clone_sector=%lu", hook->clone->bi_size, hook->clone->bi_sector );
              int segno=0;
              struct bio_vec *bvec;

              bio_for_each_segment(bvec,hook->clone,segno){

                  char *buffer = __bio_kmap_atomic(hook->clone,segno, KM_USER0);    
                  for(i=hook->clone->bi_io_vec[segno].bv_offset;i<hook->clone->bi_io_vec[segno].bv_len+hook->clone->bi_io_vec[segno].bv_offset;i++){
          //            printk("%c",buffer[i]);
                      buffer[i]=~buffer[i];
                  }
                  __bio_kunmap_atomic(buffer, KM_USER0);
                  
              }
              
              
              //printk(KERN_INFO "filter: my bio_endio count=%d",count);

              kmem_cache_free(my_hooks,hook);

              b->bi_end_io(b,error);

          };


          // write bi_end_io - unused

          static void write_endio(struct bio *b, unsigned done, int error){

              printk(KERN_INFO "filter: write_endio err=%d size=%u sector=%lu",error,done,b->bi_sector);

              b->bi_end_io(b,error);
          }


          // filter make_request_fn()
          static void filter_mrqfn(struct request_queue *rq, struct bio *b)
          {

              printk(KERN_INFO "filter: Sector=%lu Size=%d bi_rw=%lu rw_bit=%lu\n", b->bi_sector, b->bi_size, b->bi_rw, (b->bi_rw & 1));

              // direction WRITE
              if(bio_data_dir(b)==WRITE){
                  printk(KERN_INFO "filter: write Sector=%lu Size=%d bi_rw=%lu rw_bit=%lu\n", b->bi_sector, b->bi_size, b->bi_rw, (b->bi_rw & 1));

                  struct hook *hook=NULL;
                  hook = kmem_cache_alloc(my_hooks,GFP_NOIO|__GFP_NOFAIL);

                  *hook = (struct hook){
                      .old_private = b->bi_private,
                      .old_endio = b->bi_end_io,
                      .clone = bio_clone_bioset(b,GFP_NOIO,bs),
                  };
                  b->bi_end_io = read_endio;
                  b->bi_private = hook;

                  ptr=(char *)bio_data(b);
                  for(i=0;i<b->bi_size;i++)  //4096 as bio is going to be in 4kb chunk
                  {
                     //printk("%c",*ptr);
                     ptr[i]=~ptr[i];    
                  }

                  /* calling the original make_request_fn() */
                  orig_mrqfn(rq, b);


              }

              // direction READ
              if(bio_data_dir(b)==READ){

                  //printk(KERN_INFO "filter: read Sector=%lu Size=%d bi_rw=%lu rw_bit=%lu\n", b->bi_sector, b->bi_size, b->bi_rw, (b->bi_rw & 1));
                  
                  struct hook *hook=NULL;
                  hook = kmem_cache_alloc(my_hooks,GFP_NOIO|__GFP_NOFAIL);

                  *hook = (struct hook){
                      .old_private = b->bi_private,
                      .old_endio = b->bi_end_io,
                      .clone = bio_clone_bioset(b,GFP_NOIO,bs),
                  };
                  b->bi_end_io = read_endio;
                  b->bi_private = hook;
                  /* calling the original make_request_fn() */
                  orig_mrqfn(rq, b);
              
              }
          }

          /*
          * set_mrqfn() - Change the original make_request_fn() to our
          * modules request function
          */

          static void set_mrqfn(void)
          {
              struct super_block *sb;
              /* lock filesystem to prevent any further changes */
              fsync_bdev(bd);
              sb = freeze_bdev(bd);
              if (bd->bd_disk->queue->make_request_fn == filter_mrqfn) {
                  printk(KERN_INFO "filter: modules request function is already active\n");
              } else {
                  /* save the pointer to the original make_request_fn() */
                  orig_mrqfn = bd->bd_disk->queue->make_request_fn;
                  /* replace the original with our modules request function */
                  bd->bd_disk->queue->make_request_fn = filter_mrqfn;

              }
              
              /* unlock filesystem */
              thaw_bdev(bd, sb);
          }


          /*
          * restore_mrqfn() - Restore the original make_request_fn()
          */

          static void restore_mrqfn(void)
          {
              struct super_block *sb = bd->bd_super;
              if (orig_mrqfn) {
                  /* lock filesystem to prevent any further changes */
                  fsync_bdev(bd);
                  sb = freeze_bdev(bd);
                  /* restore the original request function */
                  bd->bd_disk->queue->make_request_fn = orig_mrqfn;
                  /* unlock filesystem */
                  thaw_bdev(bd, sb);
              }
              orig_mrqfn = NULL;
          }

          static int __init filter_init(void)
          {
              printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);    
              orig_mrqfn = NULL;
              // Read block device from path
              bd = blkdev_get_by_path(PARTITION_NAME, FMODE_READ, NULL);
              if (IS_ERR(bd)) {
                  printk(KERN_ERR "filter: failed to get block device\n");
                  return 0;
              }
              
              printk(KERN_INFO "filter: found block device with major number %d\n", bd->bd_disk->major);
              printk(KERN_INFO "filter: file system block size %d\n", bd->bd_block_size);
              printk(KERN_INFO "filter: start sector %lu\n", (unsigned long)bd->bd_part->start_sect);
              printk(KERN_INFO "filter: number of sectors %lu\n", (unsigned long)bd->bd_part->nr_sects);
              printk(KERN_INFO "filter: logical block size %d\n", bdev_logical_block_size(bd));
              printk(KERN_INFO "filter: physical block size %d\n", bdev_physical_block_size(bd));
              set_mrqfn();

              if(!(my_hooks = kmem_cache_create("my_hooks",sizeof(struct hook), __alignof__(struct hook),0 ,NULL))){
              return 1; // fail!!!
              };
              return 0;
          }

          static void __exit filter_exit(void)
          {
              printk(KERN_DEBUG "filter: %s\n", __FUNCTION__);
              restore_mrqfn();
              // clear HOOKs
              if(my_hooks){
              kmem_cache_destroy(my_hooks);
              }

              if (!IS_ERR(bd)) {
                  blkdev_put(bd, FMODE_READ);
              }
          }

          module_init(filter_init);
          module_exit(filter_exit);
          MODULE_LICENSE("Dual MIT/GPL");
          MODULE_AUTHOR("VV");




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру