LINUX.ORG.RU

Device-mapper target модуль ядра

 ,


0

3

На основе этой статьи разрабатываю модуль ядра для device-mapper-а. Статья написана для какой-то 2.6+ версии ядра. Мне же надо её преобразовать к версии 2.4.

Основная проблема в том, что в ядре 2.4 используется иной формат обмена данными между устройствами. Если в современных системах сообщения хранятся в структуре bio, то в 2.4 версии используется некая структура buffer_head.

Примеров использования я не нашёл, качественной документации тоже. Поэтому делал по интуиции.

Вот получившийся код (специально для сравнения версий оставил директивы define):

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/device-mapper.h>
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#include <linux/bio.h>
#endif
 
#include <linux/fs.h>
#include <linux/kdev_t.h>
 
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
#include <linux/mm.h>
#endif
 
struct Sddm_target
{
    struct dm_dev *dev;
    sector_t start;
};
 
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
static int sddm_target_map(struct dm_target *ti, struct buffer_head *bh, int rw, union map_info *map_context)
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
static int sddm_target_map(struct dm_target *ti, struct bio *bio, union map_info *map_context)
#else
static int sddm_target_map(struct dm_target *ti, struct bio *bio)
#endif
{
    struct Sddm_target *mdt;
 
    mdt = (struct Sddm_target *) ti->private;
 
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
    bh->b_dev = mdt->dev->dev;
#else
    bio->bi_bdev = mdt->dev->bdev;
#endif
 
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
    submit_bh(rw, bh);
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    submit_bio(bio->bi_rw, bio);
#else
    submit_bio(bio);
#endif
 
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
    return 0;
#else
    return DM_MAPIO_SUBMITTED;
#endif
}
 
static int sddm_target_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
    struct Sddm_target *mdt;
    unsigned long start;
    unsigned long len;
    int err;
 
    if (argc != 2)
    {
        ti->error = "Invalid argument count";
        return -EINVAL;
    }
 
    mdt = (struct Sddm_target*)kmalloc(sizeof(struct Sddm_target), GFP_KERNEL);
 
    if (mdt == NULL)
    {
        printk(KERN_CRIT "\n Mdt is null\n");
        ti->error = "dm-basic_target: Cannot allocate linear context";
        return -ENOMEM;
    }       
 
    if (sscanf(argv[1], "%lu", &start) != 1)
    {
        ti->error = "dm-basic_target: Invalid deviceee sector";
        kfree(mdt);
        printk(KERN_CRIT "\n>>out function basic_target_ctr with errorrrrrrrrrr \n");           
        return -EINVAL;
    }
 
    mdt->start = (sector_t)start;
 
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
    err = dm_get_device(ti, argv[0], ti->begin, ti->len, dm_table_get_mode(ti->table), &mdt->dev);
#else
    err = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &mdt->dev);
#endif
    if (err)
    {
        ti->error = "dm-basic_target: Device lookup failed";
        kfree(mdt);
        return -EINVAL;
    }
 
    ti->private = mdt;
 
    return 0;
}
 
static void sddm_target_dtr(struct dm_target *ti)
{
    struct Sddm_target *mdt = (struct Sddm_target *) ti->private;
 
    dm_put_device(ti, mdt->dev);
    kfree(mdt);
}
 
static struct target_type sddm_target = {
        .name = "sddm_target",
        .version = {1,0,0},
        .module = THIS_MODULE,
        .ctr = sddm_target_ctr,
        .dtr = sddm_target_dtr,
        .map = sddm_target_map,
};
 
static int __init init_sddm_target(void)
{
    int result;
 
    result = dm_register_target(&sddm_target);
 
    return 0;
}
 
static void __exit cleanup_sddm_target(void)
{
    dm_unregister_target(&sddm_target);
}
 
module_init(init_sddm_target);
 
module_exit(cleanup_sddm_target);
 
MODULE_LICENSE("GPL");

Сейчас модуль должен просто транслировать сообщения, направленные на виртуальное устройство, на реальное устройство. Это выполняется в «маппирующей» функции sddm_target_map, а именно во фрагменте:

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
    bh->b_dev = mdt->dev->dev;
#else
    bio->bi_bdev = mdt->dev->bdev;
#endif

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,32)
    submit_bh(rw, bh);
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    submit_bio(bio->bi_rw, bio);
#else
    submit_bio(bio);
#endif

То есть просто меняю целевое устройство и повторяю запрос.

Использую я представленный модуль в момент загрузки системы для создания «маппирующего» блочного устройства. Это устройство в дальнейшем должно замещать root-раздел. (Смежный вопрос).

Код для версий 2.6+ работает. Для 2.4 - нет. Виртуальное устройство создаётся, несколько раз выполняется функция «маппинга», но процесс монтирования виснет, выдавая следующее сообщение:

kjoutnald starting. Commit interval 5 seconds

Предполагаю, что ошибка кроется либо в функции-конструкторе sddm_target_ctr, либо в sddm_target_map.

Прошу знатоков 2.4 ядра и просто kernel-space программирования помочь! Спасибо!

Ответ на: комментарий от Dark_SavanT

Заказчик использует только МСВС. Оборудование лично не видел, но почти уверен, что оно примерно такого же возраста.

AccumPlus ()

Возможно эта информация будет полезна...

После старта команды mount и перед зависанием на kjournald выполняется шесть запросов к устройству (то есть шеть раз вызывается функция sddm_target_map, шесть раз приходит структура buffer_head).

Далее смотрю здесь.

buffer_head - структура, которая является дескриптором buffer-а. Однин буфер представляет один блок. Блок может хранить несколько секторов. То есть его размер >= 512 байт.

Что может передаваться в первых шести запросах, как думаете?

AccumPlus ()

И ещё кое-что...

Вот какие блоки запрашиваются:

1. Номер 1, размер 1024
2. Номер 0, размер 4096
3. Номер 1, размер 4096
4. Номер 2, размер 4096
5. Номер 1029, размер 4096
6. Номер 1547, размер 4096

Файловая система ext3.

AccumPlus ()

Не смог толком разораться в коде device-mapper для ядра 2.4 (другой документации нет), но как-то посмотрев на код linear_map() и origin_map(), увидел, что они там любят просто указать какой сектор нужно читать и делать ″return 1″.

А, если они делают ″return 0″ из map функции, то перед этим они делают ″queue_bh()″, а не ″submit_bh()″, передают задание отдельному демону (потоку) ядра . И вобще в патче они не вызывают ″submit_bh()″, а непосредственно ″generic_make_request()″...

mky ★★★★★ ()
Ответ на: комментарий от mky

Опа. Спасибо за наводку, почитаю патч.

К слову, я думаю, что заработало. Но ввиду того, что этот вопрос взаимосвязан с этим, заработало не всё...

AccumPlus ()
Ответ на: комментарий от mky

Заработал следующий код:

static int sddm_target_map(struct dm_target *ti, struct buffer_head *bh, int rw, union map_info *map_context)
{
	struct Sddm_target *mdt;       
	mdt = (struct Sddm_target *) ti->private;
	bh->b_rdev = mdt->dev->dev;
	return 1;
}

Спасибо!

AccumPlus ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.