LINUX.ORG.RU

Block device driver

 , , , ,


2

3

Привет всем!

Есть задача изготовления драйвера блочного устройства. В нём нужно делать I/O к другим дископодобным девайсам ... Гуглил, читал LDD, LDD3 ... но видимо что-то всё равно упускаю ...

Есть тут кто имеет предметный опыт написания ? В качестве первого вопроса: после инсталляции модуля в dmesg появляется «cut -here» секция, что это означает ? Что-то пошло не так, или это информационный блок ?

root@sysman:/home/sysman/Works/vCloud/block# insmod dudriver.ko
root@sysman:/home/sysman/Works/vCloud/block# 

[ 4174.458121] DUDRIVER: Initialize the driver ...
[ 4174.458123] [DUDRIVER:458] : Assigned major = 252
[ 4174.458125] [DUDRIVER:367] : [ffff97cb191ebf60] Begin setup device, mode = 0 ...
[ 4174.458238] blk_queue_max_segment_size: set to minimum 4096
[ 4174.458240] [DUDRIVER:388] : [ffff97cb191ebf60] Opening ...
[ 4174.458253] [DUDRIVER:402] : [ffff97cb191ebf60] Opened, iodevfp = ffff97cab93aef00.
[ 4174.458257] [DUDRIVER:425] : [ffff97cb191ebf60] disk_name = dua, iobuf = ffff97cace83cc00


[ 4174.458259] ------------[ cut here ]------------
[ 4174.458265] WARNING: CPU: 2 PID: 5049 at /build/linux-hwe-0vY49E/linux-hwe-4.10.0/block/genhd.c:596 device_add_disk+0x2cf/0x480
[ 4174.458266] Modules linked in: dudriver(OE+) vboxsf(OE) vboxvideo(OE) crct10dif_pclmul crc32_pclmul joydev ghash_clmulni_intel ttm drm_kms_helper drm fb_sys_fops syscopyarea sysfillrect vboxguest(OE) sysimgblt pcbc input_leds mac_hid serio_raw aesni_intel aes_x86_64 crypto_simd glue_helper i2c_piix4 cryptd intel_rapl_perf parport_pc ppdev lp parport autofs4 hid_generic usbhid hid fjes psmouse ahci e1000 libahci video
[ 4174.458283] CPU: 2 PID: 5049 Comm: insmod Tainted: G           OE   4.10.0-42-generic #46~16.04.1-Ubuntu
[ 4174.458284] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 4174.458285] Call Trace:
[ 4174.458290]  dump_stack+0x63/0x90
[ 4174.458293]  __warn+0xcb/0xf0
[ 4174.458294]  warn_slowpath_null+0x1d/0x20
[ 4174.458295]  device_add_disk+0x2cf/0x480
[ 4174.458298]  dua_init+0x2dd/0x1000 [dudriver]
[ 4174.458299]  ? 0xffffffffc033d000
[ 4174.458302]  do_one_initcall+0x53/0x1c0
[ 4174.458306]  ? kmem_cache_alloc_trace+0x152/0x1c0
[ 4174.458308]  do_init_module+0x5f/0x1ff
[ 4174.458311]  load_module+0x1825/0x1bf0
[ 4174.458312]  ? __symbol_put+0x60/0x60
[ 4174.458315]  ? ima_post_read_file+0x7d/0xa0
[ 4174.458319]  ? security_kernel_post_read_file+0x6b/0x80
[ 4174.458320]  SYSC_finit_module+0xdf/0x110
[ 4174.458321]  SyS_finit_module+0xe/0x10
[ 4174.458327]  entry_SYSCALL_64_fastpath+0x1e/0xad
[ 4174.458328] RIP: 0033:0x7fda77b60499
[ 4174.458329] RSP: 002b:00007ffe49df7778 EFLAGS: 00000206 ORIG_RAX: 0000000000000139
[ 4174.458330] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007fda77b60499
[ 4174.458331] RDX: 0000000000000000 RSI: 0000563ba88dc26b RDI: 0000000000000003
[ 4174.458331] RBP: 00007ffe49df6730 R08: 0000000000000000 R09: 00007fda77e25ea0
[ 4174.458332] R10: 0000000000000003 R11: 0000000000000206 R12: 000000000000006a
[ 4174.458332] R13: 0000563ba92261d0 R14: 0000563ba9226130 R15: 00007ffe49df65fc
[ 4174.458333] ---[ end trace 9676674954dab3b5 ]---
[ 4174.459946] [DUDRIVER:434] : [ffff97cb191ebf60] setup device end (dua)
[ 4174.459947] DUDRIVER: End the driver initialization.
root@sysman:/home/sysman/Works/vCloud/block# 

Следующая проблема связана с I/O из модуля, похоже что нет возврата из vfs_read/vfs_write , сужу по dmesg, не выходим на «Checking read/write status».

/**
 * @brief xfer	- Handle an I/O request.
 *
 * @param dcb	- device context block
 *
 * @param lbn	- Logical Block Number
 * @param bcnt	- a length of the data in the I/O buffer
 * @param buff	- I/O buffer
 * @param iodir	- I/O direction: 0 - READ, 1 - WRITE
 *
 * @return	- 0 > : bytes count has beed read/wrote
 *		- 0 <= errno
 */
static int xfer		(
		DRIVER_CTX *	dctx,
		sector_t	lbn,
		size_t	 	bcnt,
		char *		iobuf,
		int		iodir
				)
{
loff_t	pos = lbn * DUDRV$K_BLKSZ;
size_t	status64 = bcnt;
mm_segment_t	old_fs;

	$TRACE( ": [%p] Start I/O : iodevfp=%p lbn=%lu bcnt=%lu iobuf=%p iodir=%d", dctx, dctx->iodevfp, lbn, bcnt, iobuf, iodir);

	if ( !dctx || !dctx->iodevfp )
		{
		printk(KERN_ERR  __MODULE__ ": [%p] %s(%p) -> EIO\n", dctx,
			iodir == WRITE ? "write" : "read", dctx->iodevfp);
		return	-EIO;
		}

	if ( iodir == WRITE )
		copy_to_user (dctx->iobuf, iobuf, DUDRV$K_BLKSZ);

	old_fs = get_fs();
	set_fs(get_ds());

	if ( iodir == WRITE )
		status64 = vfs_write(dctx->iodevfp, dctx->iobuf, bcnt, &pos);
	else	status64 = vfs_read(dctx->iodevfp, dctx->iobuf, bcnt, &pos);

	set_fs( old_fs );

	$TRACE("Checking read/write status = %ld ...", status64);

	if ( bcnt != status64 )
		printk(KERN_ERR  __MODULE__ ": [%p] %s(%p, %lu octets, pos = %llu) -> %lu\n", dctx,
		       iodir == WRITE ? "write" : "read", dctx->iodevfp, bcnt, pos, status64);

	if ( iodir == READ )
		copy_from_user (iobuf, dctx->iobuf, DUDRV$K_BLKSZ);

	$TRACE( ": [%p] End I/O : pos=%llu bcnt=%lu iobuf=%p iodir=%d, status = %ld", dctx, pos, bcnt, iobuf, iodir, status64);

	return	status64;
}

Буду благодарен за любую помощь.

после инсталляции модуля в dmesg появляется «cut -here» секция, что это означает ? Что-то пошло не так, или это информационный блок ?

Там же написано:

WARNING: CPU: 2 PID: 5049 at /build/linux-hwe-0vY49E/linux-hwe-4.10.0/block/genhd.c:596 

Это предупреждение. Хочешь узнать больше - смотришь block/genhd.c:596 ядра, которое у тебя используется.

Следующая проблема связана с I/O из модуля, похоже что нет возврата из vfs_read/vfs_write

Там вообще всё плохо - коды возврата из copy_*_user не проверяются, set_fs/get_fs/vfs_{read,write}. Модуль блочного устройства обращается к VFS?

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

genhd.c - ох, просмотрел в этой вермишели ... Спсб.

По copy_to/from_usr - добавил проверки ... Спсб за наколку. Ещё дополнительно обклал их set_fs() ... и вообще всё заработало. :-)

Итого:

/**
 * @brief xfer	- Handle an I/O request.
 *
 * @param dcb	- device context block
 *
 * @param lbn	- Logical Block Number
 * @param bcnt	- a length of the data in the I/O buffer
 * @param buff	- I/O buffer
 * @param iodir	- I/O direction: 0 - READ, 1 - WRITE
 *
 * @return	- 0 > : bytes count has beed read/wrote
 *		- 0 <= errno
 */
static int xfer		(
		DRIVER_CTX *	dctx,
		sector_t	lbn,
		size_t	 	bcnt,
		char *		iobuf,
		int		iodir
				)
{
loff_t	pos = lbn * DUDRV$K_BLKSZ;
size_t	status64 = 0;
mm_segment_t	old_fs;
int	status;


	$TRACE( ": [%p] Start I/O : iodevfp=%p lbn=%lu bcnt=%lu iobuf=%p iodir=%d", dctx, dctx->iodevfp, lbn, bcnt, iobuf, iodir);

	if ( !dctx || !dctx->iodevfp )
		{
		printk(KERN_ERR  __MODULE__ ": [%p] %s(%p) -> EIO\n", dctx,
			iodir == WRITE ? "write" : "read", dctx->iodevfp);
		return	-EIO;
		}

	if ( iodir == WRITE )
		{
		old_fs = get_fs();
		set_fs( get_ds() );

		status = copy_to_user (dctx->iobuf, iobuf, DUDRV$K_BLKSZ);

		set_fs( old_fs );

		if ( status )
			{
			printk(KERN_ERR  __MODULE__ ": [%p] copy_to_user() -> %d", dctx, status);
			return	-EBADR;
			}
		}

	old_fs = get_fs();
	set_fs( get_ds() );

	if ( iodir == WRITE )
		status64 = vfs_write(dctx->iodevfp, dctx->iobuf, bcnt, &pos);
	else	status64 = vfs_read(dctx->iodevfp, dctx->iobuf, bcnt, &pos);

	set_fs( old_fs );

	$TRACE("Checking read/write status = %ld ...", status64);

	if ( bcnt != status64 )
		printk(KERN_ERR  __MODULE__ ": [%p] %s(%p, %lu octets, pos = %llu) -> %lu\n", dctx,
		       iodir == WRITE ? "write" : "read", dctx->iodevfp, bcnt, pos, status64);

	if ( iodir == READ )
		{
		old_fs = get_fs();
		set_fs( get_ds() );

		status = copy_from_user (iobuf, dctx->iobuf, DUDRV$K_BLKSZ);

		set_fs( old_fs );

		if ( status )
			{
			printk(KERN_ERR  __MODULE__ ": [%p] copy_from_user() -> %d", dctx, status);
			return	-EBADR;
			}
		}

	$TRACE( ": [%p] End I/O : pos=%llu bcnt=%lu iobuf=%p iodir=%d, status = %ld", dctx, pos, bcnt, iobuf, iodir, status64);

	return	status64;
}



Большое спасибо за помощь!
SysMan ()
Ответ на: комментарий от SysMan

Ещё дополнительно обклал их set_fs() ... и вообще всё заработало. :-)

Печально.

Большое спасибо за помощь!

Из того, что я написал, самое полезное - вопрос «Модуль блочного устройства обращается к VFS?». Это намек на то, что так делать нельзя. То, что тебе приходится пользоваться get_fs/set_fs (я ими за 15 лет работы не пользовался ни разу) - это признак того, что ты всё делаешь не так.

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

Тогда , пожалуйста конкретнее. Использование vfs_read/write - единственно найденный подтверждённый. Есть другие ? Я весь внимания.

Разумеется я бы предпочёл миновать VFS и прочая, но напрямую работать с дривером целевого блочного устройства. Есть такой способ ?

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

Использование vfs_read/write - единственно найденный подтверждённый

Подтвержденный кем? Не слушай его больше.

Разумеется я бы предпочёл миновать VFS и прочая,

«Предпочел»? Ты _обязан_ это сделать.

но напрямую работать с дривером целевого блочного устройства.

«Целевого»? Я думал, ты его сам пишешь.

Есть такой способ ?

Конечно. У драйвера блочного устройства есть интерфейс. Описан в LDD3: https://static.lwn.net/images/pdf/LDD3/ch16.pdf (да, я знаю, что книга устарела, но для понимания концепции она годится). Как ты мог это упустить при чтении, я просто не понимаю.

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

Дружище, спасибо за диалог конечно, но ... не скупись. Главу 16 я прочел. Книжка протухла спустя неделю после выпуска небось, и методы описанные там не работают. Шняга а-ля sbull без перепилки не заработает.

Что до того, что я пишу, я вроде написал что пишу, но могу ещё раз - делаю дривер , который переадресует преодобработанные запросы к блочному устройству.

Как получить доступ к интерфейсу? Вот каков был вопрос.

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

Шняга а-ля sbull без перепилки не заработает.

Если ты хочешь научиться писать блочные драйверы, перепиливание sbull под новое ядро - отличное упражнение.

Как получить доступ к интерфейсу?

Интерфейсу чего?

дривер

Это слово пишется «драйвер».

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

«Если ты хочешь научиться писать блочные драйверы, перепиливание sbull под новое ядро - отличное упражнение.»

Пустое.

«Интерфейсу чего?» - :-)

Ну это, то что можно пользовать заместо vfs_рутин.

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

Если ты хочешь научиться писать блочные драйверы, перепиливание sbull под новое ядро - отличное упражнение.

Пустое.

Ну, как скажешь.

Ну это, то что можно пользовать заместо vfs_рутин.

Главы 12 и 13. И глава 16, конечно.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

USB и PCI мне не нужны.

Интерфейсом дривера обычно обзываеся таки таблица рутин, ну там что-то типа open/read/write/ioctl ...

Если я не могу добраться до интерфейса драйвера скази или сата, что бы прям туда сформировать запрос - то я пока не нашёл метода (не считая fs_read/write ). Скорей всего есть такой способ.

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

USB и PCI мне не нужны.

А что тебе нужно? Чем для «изготовления драйвера блочного устройства» не подходит просто запись в память, как в sbull?

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

Если тебе нужны SCSI или ATA, то интерфейсы к ним (специфичные для обоих случаев) описаны в документации к ядру.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Я же написал мне нужно принятый моим дривером блок - отправить на запись/чтение в другое блочное устройство.

sbull - демонстрирует интерфейс (комплект рутин/мэтодов) к блочному устройству , как он видиться из верхнего уровня, как учебная задача это проходиться за 2 академчаса с контрольной работой (пиво инклюдед). sbull - не демонстрирует логики, с помошью которой, не используя штатный I/O интерфейс а-ля sys_read/write (или vfs_read/write) взять req/bio и поставить его в очередь запросов например к девайсу /dev/sdb, используя же только интерфейс блочного устройства.

Что бы было понятнее, есть базовый интерфейс, он описывается такой шнягой: struct block_device_operations

Всё было бы замечательно, если бы в этой байде (block_device_ops) был явным образом вкорячен метод а-ля «qio» (Enqueue I/O - название выдуманное), тогда остаётся найти через какой-нибудь API способ добывать этот интерфейс для нужного блок-девайса и, используя, qio - перепуливать req/bio в нужный девайс, минуя такую хрень как копирование буферов юзер/кернел space и прочая.

Я вполне допускаю что есть какой-то API, посредством которого можно добыть входы рутины для обозначенной нужды.

PS:Ты речь ведёшь пр интерфейсы к контроллерам (а-ля SCSI, ATA, IDE, DSSI, VAXBI, FC и ещё хрен знает какие) - они мне не треба. :-)

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

учебная задача это проходиться за 2 академчаса с контрольной работой

А. Ну тогда говнокод, который ты сделал, вполне подходит. Думаю, тот, кто выделил на это 2 академических часа, вовсе не предполагал нормальной реализации (через очереди запросов).

дривером

рутин

мэтодов

Перестань коверкать язык. Такой текст - неуважение к тому, кто пытается его читать.

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

«вовсе не предполагал нормальной реализации (через очереди запросов).» - ты не понимаешь о чём говоришь.

Спасибо за участие. Я бы послушал кого-то поопытнее.

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

Дальше, не учи уважению , коли тыкаешь не знакомуму человек.

Это Интернет и я намного старше. Не вижу причин обращаться к тебе на «вы» и не ожидаю такого обращения к себе.

У тебя есть что по теме ?

Если ты хотел спросить «ты писал драйверы для layered блочных устройств», то нет. Но если бы надо было - написал бы.

Спасибо за участие.

Да вроде бы не за что.

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

Намного старше кого? А даже если так, то чё ? Сходу ментора включаешь ? Рановато, ответы пока твои были не впопад.

Я спросил ровно то, что спросил, и не обязан перед тобой растекаться, разжёвывая и запихивая в рот, чай не школьник и не студент перед сансеем.

«Да вроде бы не за что.» - я знаю, только из вежливости.

SysMan ()

Я пишу блочные драйвера иногда. tailgunner всё правильно говорит. (и насчёт коверканья языка я с ним полностью согласен).

Ты вообще чего хочешь то? Какое ядро? Почитай какой-нибудь драйвер блочный если не хочешь устаревшие книжки читать. Почитай null_blk или scsi_debug напирмер.

anonymous ()

Наваял говнокод, показал людям, почему теперь удивляешься, что тебя в него тыкают более опытные товарищи?

Если никогда раньше блочных драйверов не писал, то в ядре полно примеров в каталоге drivers/block. Там ничего сложного, в общем-то.

mv ★★★★★ ()
Ответ на: комментарий от SysMan
«Да вроде бы не за что.» - я знаю, только из вежливости.


чувак, ну по всем же видно, что ты невероятно крут и рюхаст для этого форума, ну не проверяешь ты код возврата функций (прости, когда хотел обозвать их «рутинами» чуть не блеванул), дык то вообще какой-то отщепенец придумал в далеком 1981-ом, настоящие мужыки давно уже так не делают - на дворе-то XXI-й век, в конце концов.

из уважения к твоему интеллекту, не буду тыкать тебя носом, как маленького котенка в его гавно, но спасибо, что напомнил одно старое выражение, «не мале, а всралося» :)

metawishmaster ★★★ ()