На основе этой статьи разрабатываю модуль ядра для 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 программирования помочь! Спасибо!