The OpenNET Project / Index page

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



"Фильтр block device в Линукс"
Вариант для распечатки  
Пред. тема | След. тема 
Форум Программирование под UNIX (Разное)
Изначальное сообщение [ Отслеживать ]

"Фильтр block device в Линукс"  +/
Сообщение от DoubleHead (ok), 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");

Ответить | Правка | Cообщить модератору

Оглавление

Сообщения по теме [Сортировка по времени | RSS]


1. "Фильтр block device в Линукс"  +/
Сообщение от Павел Самсоновemail (?), 26-Июн-15, 20:13 
>[оверквотинг удален]
>  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 будет.

Ответить | Правка | ^ к родителю #0 | Наверх | Cообщить модератору

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

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

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

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

Ответить | Правка | ^ к родителю #1 | Наверх | Cообщить модератору

2. "Фильтр block device в Линукс"  +/
Сообщение от Павел Самсоновemail (?), 26-Июн-15, 20:31 
>[оверквотинг удален]
>  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?

Ответить | Правка | ^ к родителю #0 | Наверх | Cообщить модератору

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

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

Ответить | Правка | ^ к родителю #2 | Наверх | Cообщить модератору

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

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

Ответить | Правка | ^ к родителю #4 | Наверх | Cообщить модератору

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

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

Ответить | Правка | ^ к родителю #5 | Наверх | Cообщить модератору

7. "Фильтр block device в Линукс"  +/
Сообщение от Doublehead (?), 13-Ноя-18, 21:44 
>>> Диск примонтирован? Может дисковый кэш. Что будет при команде 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");

Ответить | Правка | ^ к родителю #5 | Наверх | Cообщить модератору

Архив | Удалить

Рекомендовать для помещения в FAQ | Индекс форумов | Темы | Пред. тема | След. тема




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

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