This shows you the differences between two versions of the page.
so2:laboratoare:lab07 [2017/04/02 20:29] octavian.purdila [Așteptarea încheierii unei structuri bio] update lxr links to 4.9; add submit_bio_wait |
so2:laboratoare:lab07 [2018/05/02 12:31] (current) theodor.stoican [Structura block_device_operations] |
||
---|---|---|---|
Line 3: | Line 3: | ||
===== Obiectivele laboratorului ===== | ===== Obiectivele laboratorului ===== | ||
- | *dobândirea de cunoștințe legate de funcționarea subsistemului de I/O pe Linux | + | * dobândirea de cunoștințe legate de funcționarea subsistemului de I/O pe Linux |
- | *acomodarea cu structurile și funcțiile de lucru cu dispozitive de tip bloc | + | * acomodarea cu structurile și funcțiile de lucru cu dispozitive de tip bloc |
- | *obținerea unor deprinderi de bază de utilizare a API-ului pentru dispozitive de tip bloc prin rezolvarea exercițiilor | + | * obținerea unor deprinderi de bază de utilizare a API-ului pentru dispozitive de tip bloc prin rezolvarea exercițiilor |
===== Cuvinte cheie ===== | ===== Cuvinte cheie ===== | ||
- | *dispozitive de tip block | + | * dispozitive de tip block |
- | *subsistemul de I/O | + | * subsistemul de I/O |
- | *înregistrare/deînregistrare | + | * înregistrare/deînregistrare |
- | *''struct gendisk'' | + | * ''struct gendisk'' |
- | *sector | + | * sector |
- | *''struct block_device_operations'' | + | * ''struct block_device_operations'' |
- | *cereri -- ''struct request'' | + | * cereri -- ''struct request'' |
- | *cozi de cereri -- ''struct request_queue'' | + | * cozi de cereri -- ''struct request_queue'' |
- | *prelucrarea unei cereri | + | * prelucrarea unei cereri |
- | *''struct bio'', ''submit_bio'' | + | * ''struct bio'', ''submit_bio'' |
===== Materiale ajutătoare ===== | ===== Materiale ajutătoare ===== | ||
- | *[[http://elf.cs.pub.ro/so2/res/laboratoare/lab07-slides.pdf | Slide-uri de suport pentru laborator]] | + | * [[http://elf.cs.pub.ro/so2/res/laboratoare/lab07-slides.pdf | Slide-uri de suport pentru laborator]] |
- | *[[http://elf.cs.pub.ro/so2/res/extra/so2_reference.pdf | SO2 Reference Card]] | + | * [[http://elf.cs.pub.ro/so2/res/extra/so2_reference.pdf | SO2 Reference Card]] |
===== Noțiuni generale ===== | ===== Noțiuni generale ===== | ||
Line 35: | Line 35: | ||
===== Device drivere de tip bloc în Linux ===== | ===== Device drivere de tip bloc în Linux ===== | ||
+ | |||
==== Înregistrarea unui dispozitiv de tip bloc ==== | ==== Înregistrarea unui dispozitiv de tip bloc ==== | ||
Line 69: | Line 70: | ||
} | } | ||
</code> | </code> | ||
+ | |||
==== Înregistrarea unui disc ==== | ==== Înregistrarea unui disc ==== | ||
Line 130: | Line 132: | ||
Structura [[http://lxr.free-electrons.com/source/include/linux/genhd.h?v=4.9#L180 | struct gendisk]] are următoarele câmpuri importante: | Structura [[http://lxr.free-electrons.com/source/include/linux/genhd.h?v=4.9#L180 | struct gendisk]] are următoarele câmpuri importante: | ||
- | *''major'', ''first_minor'', ''minors'', care descriu identificatorii folosiți de disc; un disc trebuie să aibă cel puțin un minor; dacă discul permite operația de partiționare, trebuie alocat un minor pentru fiecare partiție posibilă | + | * ''major'', ''first_minor'', ''minors'', care descriu identificatorii folosiți de disc; un disc trebuie să aibă cel puțin un minor; dacă discul permite operația de partiționare, trebuie alocat un minor pentru fiecare partiție posibilă |
- | *''disk_name'', care reprezintă numele discului, așa cum apare în ''/proc/partitions'' și în ''sysfs'' (''/sys/block'') | + | * ''disk_name'', care reprezintă numele discului, așa cum apare în ''/proc/partitions'' și în ''sysfs'' (''/sys/block'') |
- | *''fops'', care reprezintă operațiile asociate discului | + | * ''fops'', care reprezintă operațiile asociate discului |
- | *''queue'', care reprezintă coada de cereri ((Despre structura struct request_queue și operațiile pentru prelucrarea cozilor de cereri se va discuta în secțiunea [[#cozi_de_cereri | Cozi de cereri]])) | + | * ''queue'', care reprezintă coada de cereri ((Despre structura struct request_queue și operațiile pentru prelucrarea cozilor de cereri se va discuta în secțiunea [[#cozi_de_cereri | Cozi de cereri]])) |
- | *''capacity'', care reprezintă capacitatea discului în sectoare de 512 octeți; se inițializează folosind funcția [[http://lxr.free-electrons.com/source/include/linux/genhd.h?v=4.9#L451 | set_capacity]] | + | * ''capacity'', care reprezintă capacitatea discului în sectoare de 512 octeți; se inițializează folosind funcția [[http://lxr.free-electrons.com/source/include/linux/genhd.h?v=4.9#L451 | set_capacity]] |
- | *''private_data'', care reprezintă un pointer către datele private | + | * ''private_data'', care reprezintă un pointer către datele private |
Un exemplu de completare a unei structuri [[http://lxr.free-electrons.com/source/include/linux/genhd.h?v=4.9#L180 | struct gendisk]] este prezentat în continuare: | Un exemplu de completare a unei structuri [[http://lxr.free-electrons.com/source/include/linux/genhd.h?v=4.9#L180 | struct gendisk]] este prezentat în continuare: | ||
Line 251: | Line 253: | ||
} | } | ||
- | static int my_block_release(struct gendisk *gd, fmode_t mode) | + | static void my_block_release(struct gendisk *gd, fmode_t mode) |
{ | { | ||
//... | //... | ||
- | |||
- | return 0; | ||
} | } | ||
Line 349: | Line 349: | ||
Funcțiile utilizate pentru prelucrarea cererilor din coadă, descrise mai jos, sunt: | Funcțiile utilizate pentru prelucrarea cererilor din coadă, descrise mai jos, sunt: | ||
- | *[[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2333| blk_peek_request]] - obține o referință la prima cerere din coadă; cererea trebuie pornită folosind [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2460 | blk_start_request]]; | + | * [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2333| blk_peek_request]] - obține o referință la prima cerere din coadă; cererea trebuie pornită folosind [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2460 | blk_start_request]]; |
- | *[[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2460 | blk_start_request]] - extrage cererea din coadă și o pornește pentru procesare; în general funcția primește ca referință un pointer la o cerere întors de [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2333|blk_peek_request]]; | + | * [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2460 | blk_start_request]] - extrage cererea din coadă și o pornește pentru procesare; în general funcția primește ca referință un pointer la o cerere întors de [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2333|blk_peek_request]]; |
- | *[[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2491 | blk_fetch_request]] - obține prima cerere din coadă (folosind [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2333 | blk_peek_request]]) și o pornește (folosind [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2460|blk_start_request]]); | + | * [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2491 | blk_fetch_request]] - obține prima cerere din coadă (folosind [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2333 | blk_peek_request]]) și o pornește (folosind [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2460|blk_start_request]]); |
- | *[[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L1334 | blk_requeue_request]] - pentru reinserarea cererii în coadă. | + | * [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L1334 | blk_requeue_request]] - pentru reinserarea cererii în coadă. |
Înainte de a apela aceste funcții, trebuie obținut spinlock-ul asociat cozii. Dacă aceste funcții sunt apelate în funcția de tip [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L224|request_fn_proc]], spinlock-ul este deja deținut. | Înainte de a apela aceste funcții, trebuie obținut spinlock-ul asociat cozii. Dacă aceste funcții sunt apelate în funcția de tip [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L224|request_fn_proc]], spinlock-ul este deja deținut. | ||
+ | |||
==== Cereri pentru dispozitive de tip bloc ==== | ==== Cereri pentru dispozitive de tip bloc ==== | ||
Line 360: | Line 361: | ||
Câmpurile structurii [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L88 | struct request]] includ: | Câmpurile structurii [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L88 | struct request]] includ: | ||
- | *''cmd_flags'', o serie de flag-uri printre care și direcția (citire sau scriere); pentru a afla direcția se folosește macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L614 | rq_data_dir]], care returnează 0 pentru o cerere de citire și 1 pentru o cerere de scriere pe dispozitiv; | + | * ''cmd_flags'', o serie de flag-uri printre care și direcția (citire sau scriere); pentru a afla direcția se folosește macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L614 | rq_data_dir]], care returnează 0 pentru o cerere de citire și 1 pentru o cerere de scriere pe dispozitiv; |
- | *''%%__sector%%'', primul sector al cererii de transfer; dacă sectorul dispozitivului are altă dimensiune, trebuie facută conversia corespunzătoare; pentru accesarea acestui câmp se folosește macro-ul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L855 | blk_rq_pos]]; | + | * ''%%__sector%%'', primul sector al cererii de transfer; dacă sectorul dispozitivului are altă dimensiune, trebuie facută conversia corespunzătoare; pentru accesarea acestui câmp se folosește macro-ul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L855 | blk_rq_pos]]; |
- | *''%%__data_len%%'', numărul total de octeți de transferat; pentru accesarea acestui câmp se folosește macro-ul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L860 | blk_rq_bytes]]; | + | * ''%%__data_len%%'', numărul total de octeți de transferat; pentru accesarea acestui câmp se folosește macro-ul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L860 | blk_rq_bytes]]; |
- | *în general, se vor transfera date din [[#Structura bio | bio-ul curent]]; dimensiunea datelor se obține cu ajutorul macro-ului [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L865 | blk_rq_cur_bytes ]]; | + | * în general, se vor transfera date din [[#Structura bio | bio-ul curent]]; dimensiunea datelor se obține cu ajutorul macro-ului [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L865 | blk_rq_cur_bytes ]]; |
- | *''bio'', o listă dinamică de structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] care reprezintă un set de bufere asociate cu cererea; acest câmp se accesează cu ajutorul macrodefiniției [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L754 | rq_for_each_segment]] pentru cazul în care există mai multe buffere sau cu macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L113|bio_data]] pentru cazul în care există un singur bufer asociat; ''bio_data'' intoarce adresa buferului asociat cererii | + | * ''bio'', o listă dinamică de structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] care reprezintă un set de bufere asociate cu cererea; acest câmp se accesează cu ajutorul macrodefiniției [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L754 | rq_for_each_segment]] pentru cazul în care există mai multe buffere sau cu macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L113|bio_data]] pentru cazul în care există un singur bufer asociat; ''bio_data'' intoarce adresa buferului asociat cererii |
* despre structura bio și operațiile asociate se va discuta în secțiunea [[#structura_bio | Structura bio]] | * despre structura bio și operațiile asociate se va discuta în secțiunea [[#structura_bio | Structura bio]] | ||
Line 381: | Line 382: | ||
Partea centrală a unui driver de tip bloc este metoda de tip [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L224 | request_fn_proc]]. În exemplele anterioare, funcția care satisfăcea acest rol era ''my_block_request''. După cum s-a precizat și în secțiunea [[#Crearea și ștergerea cozii de cereri | Crearea și ștergerea cozii de cereri]], această funcție este atașată driverului prin apelarea [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L781 | blk_init_queue]]. | Partea centrală a unui driver de tip bloc este metoda de tip [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L224 | request_fn_proc]]. În exemplele anterioare, funcția care satisfăcea acest rol era ''my_block_request''. După cum s-a precizat și în secțiunea [[#Crearea și ștergerea cozii de cereri | Crearea și ștergerea cozii de cereri]], această funcție este atașată driverului prin apelarea [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L781 | blk_init_queue]]. | ||
- | Această metodă este apelată atunci când kernel-ul consideră că driverul trebuie să proceseze cereri de I/O. Metoda trebuie să pornească procesarea cererilor din coada, dar nu este obligată să le și termine, cererile putând fi terminate din alte părți ale driverului. | + | Această metodă este apelată atunci când kernel-ul consideră că driverul trebuie să proceseze cereri de I/O. Metoda trebuie să pornească procesarea cererilor din coadă, dar nu este obligată să le și termine, cererile putând fi terminate din alte părți ale driverului. |
Parametrul ''lock'', transmis la crearea unei cozi de cereri, reprezintă un spinlock pe care kernel-ul îl deține atunci când execută metoda ''request''. Din acest motiv, metoda ''request'' rulează în context atomic și trebuie să respecte regulile pentru cod atomic (nu trebuie să apeleze funcții care pot duce la sleep etc.). Acest lock asigură și faptul că nu vor fi adăugate în coada alte cereri pentru device în timp ce se execută metoda request. | Parametrul ''lock'', transmis la crearea unei cozi de cereri, reprezintă un spinlock pe care kernel-ul îl deține atunci când execută metoda ''request''. Din acest motiv, metoda ''request'' rulează în context atomic și trebuie să respecte regulile pentru cod atomic (nu trebuie să apeleze funcții care pot duce la sleep etc.). Acest lock asigură și faptul că nu vor fi adăugate în coada alte cereri pentru device în timp ce se execută metoda request. | ||
Line 400: | Line 401: | ||
break; | break; | ||
- | if (rq->cmd_type != REQ_TYPE_FS) { | + | if (blk_rq_is_passthrough(rq)) { |
printk (KERN_NOTICE "Skip non-fs request\n"); | printk (KERN_NOTICE "Skip non-fs request\n"); | ||
__blk_end_request_all(rq, -EIO); | __blk_end_request_all(rq, -EIO); | ||
Line 433: | Line 434: | ||
struct bio { | struct bio { | ||
//... | //... | ||
- | struct block_device *bi_bdev; | + | struct gendisk *bi_disk; |
unsigned int bi_opf; /* bottom bits req flags, top bits REQ_OP. Use accessors. */ | unsigned int bi_opf; /* bottom bits req flags, top bits REQ_OP. Use accessors. */ | ||
//... | //... | ||
Line 444: | Line 445: | ||
}; | }; | ||
</code> | </code> | ||
+ | |||
La rândul ei, structura [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] conține un vector ''bi_io_vec'' de tipul [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L29 | struct bio_vec]]. Acesta este format din paginile individuale din memoria fizică ce trebuie transferate, offsetul în cadrul paginii și dimensiunea buferului. Pentru a parcurge o structura ''bio'', trebuie parcurs acest vector de structuri [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L29 | struct bio_vec]] și transferate datele din fiecare pagină fizică. Pentru a simplifica parcurgerea vectorului se folosește [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L35|struct bvec_iter]]. Această structură menține informații despre câte bufere și sectoare au fost consumate în timpul parcurgerii. Tipul cererii este encodat în câmpul ''bi_opf'', pentru determinarea acestuia folosiți funcția [[http://lxr.free-electrons.com/source/include/linux/fs.h?v=4.9#L2507|bio_data_dir]]. | La rândul ei, structura [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] conține un vector ''bi_io_vec'' de tipul [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L29 | struct bio_vec]]. Acesta este format din paginile individuale din memoria fizică ce trebuie transferate, offsetul în cadrul paginii și dimensiunea buferului. Pentru a parcurge o structura ''bio'', trebuie parcurs acest vector de structuri [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L29 | struct bio_vec]] și transferate datele din fiecare pagină fizică. Pentru a simplifica parcurgerea vectorului se folosește [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L35|struct bvec_iter]]. Această structură menține informații despre câte bufere și sectoare au fost consumate în timpul parcurgerii. Tipul cererii este encodat în câmpul ''bi_opf'', pentru determinarea acestuia folosiți funcția [[http://lxr.free-electrons.com/source/include/linux/fs.h?v=4.9#L2507|bio_data_dir]]. | ||
Line 449: | Line 451: | ||
Pentru crearea unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] se pot folosi două funcții: | Pentru crearea unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] se pot folosi două funcții: | ||
- | *[[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L391 | bio_alloc]] - alocă spațiu pentru o nouă structură; structura trebuie inițializată; | + | * [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L391 | bio_alloc]] - alocă spațiu pentru o nouă structură; structura trebuie inițializată; |
- | *[[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L396 | bio_clone]] - realizează o copie a unei structuri bio existente; structura nou obținută este inițializată cu valorile câmpurilor structurii clonate; buferele sunt partajate cu structura bio care a fost clonată astfel încât accesul la bufere trebuie făcut cu atenție pentru a evita accesul la aceași zonă de memorie din cele două clone | + | * [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L396 | bio_clone]] - realizează o copie a unei structuri bio existente; structura nou obținută este inițializată cu valorile câmpurilor structurii clonate; buferele sunt partajate cu structura bio care a fost clonată astfel încât accesul la bufere trebuie făcut cu atenție pentru a evita accesul la aceași zonă de memorie din cele două clone |
Ambele funcții întorc o nouă structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]]. | Ambele funcții întorc o nouă structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]]. | ||
Line 459: | Line 461: | ||
Pentru transmiterea unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] către driverul dispozitivului I/O asociat se folosește funcția [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2058 | submit_bio]]. Funcția primește ca argument o structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] inițializată care va fi adăugată unei cereri din coada de cereri a unui dispozitiv I/O. Din acea coadă de cereri va putea fi prelucrată de driverul dispozitivului I/O cu o funcție specializată. | Pentru transmiterea unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] către driverul dispozitivului I/O asociat se folosește funcția [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2058 | submit_bio]]. Funcția primește ca argument o structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] inițializată care va fi adăugată unei cereri din coada de cereri a unui dispozitiv I/O. Din acea coadă de cereri va putea fi prelucrată de driverul dispozitivului I/O cu o funcție specializată. | ||
+ | |||
==== Așteptarea încheierii unei structuri bio ==== | ==== Așteptarea încheierii unei structuri bio ==== | ||
Line 464: | Line 467: | ||
Pentru a fi notificați atunci când se încheie prelucrarea unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] (atunci când nu folosim ''submit_bio_wait''), va trebui folosit câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L52 | bi_end_io]] al structurii. În acest câmp se precizează funcția care va fi apelată la încheierea prelucrării structurii bio. Pentru a pasa informații către funcție se poate folosi câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=3.13#L64 | bi_private]] al structurii. | Pentru a fi notificați atunci când se încheie prelucrarea unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] (atunci când nu folosim ''submit_bio_wait''), va trebui folosit câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L52 | bi_end_io]] al structurii. În acest câmp se precizează funcția care va fi apelată la încheierea prelucrării structurii bio. Pentru a pasa informații către funcție se poate folosi câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=3.13#L64 | bi_private]] al structurii. | ||
- | |||
- | |||
==== Inițializarea unei structuri bio ==== | ==== Inițializarea unei structuri bio ==== | ||
- | După ce o structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L46 | bio]] a fost alocată și înainte de a fi transmisă, trebuie inițializată. | + | După ce o structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] a fost alocată și înainte de a fi transmisă, trebuie inițializată. |
- | Inițializarea structurii presupune completarea câmpurilor importante. După cum s-a precizat anterior, câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L71 | bi_end_io]] este folosit pentru a preciza funcția apelată la încheierea prelucrării structurii. Câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=3.13#L64 | bi_private]] este folosit pentru a stoca date utile ce pot fi accesate în funcția [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L71 | bi_end_io]]. | + | Inițializarea structurii presupune completarea câmpurilor importante. După cum s-a precizat anterior, câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L52 | bi_end_io]] este folosit pentru a preciza funcția apelată la încheierea prelucrării structurii. Câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L54 | bi_private]] este folosit pentru a stoca date utile ce pot fi accesate în funcția [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L52 | bi_end_io]]. |
- | Câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L51 | bi_rw]] specifică dacă este vorba de o operație de scriere (''1'') sau de citire (''0''). | + | Câmpul [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L29 | bi_opf]] specifică tipul operației. Folosiți [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L95|bio_set_op_attrs]] pentru a inițializa tipul operației. |
- | + | ||
- | Tot din exemplul din [[http://lxr.free-electrons.com/source/drivers/md/md.c?v=3.13#L779 | drivers/md/md.c]] se poate observa inițializarea a celorlalte câmpuri utile: | + | |
<code c> | <code c> | ||
- | int sync_page_io(struct block_device *bdev, sector_t sector, int size, | ||
- | struct page *page, int rw) | ||
- | { | ||
struct bio *bio = bio_alloc(GFP_NOIO, 1); | struct bio *bio = bio_alloc(GFP_NOIO, 1); | ||
//... | //... | ||
- | bio->bi_bdev = bdev; | + | bio->bi_disk = bdev->bd_disk; |
- | bio->bi_sector = sector; | + | bio->bi_iter.bi_sector = sector; |
- | bio_add_page(bio, page, size, 0); | + | bio_set_op_attrs(bio, REQ_OP_READ, 0); |
+ | bio_add_page(bio, page, size, offset); | ||
//... | //... | ||
</code> | </code> | ||
- | În extrasul de mai sus se specifică dispozitivul de tip bloc către care va fi trimis bio-ul, sectorul de început și conținutul. Conținutul unui bio este dat de o [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=3.13#L44 | pagină]]. | + | În extrasul de mai sus se specifică dispozitivul de tip bloc către care va fi trimis bio-ul, sectorul de început, operația (''REQ_OP_READ'' or ''REQ_OP_WRITE'') și conținutul. Conținutul unui bio este un bufer descris prin: o [[http://lxr.free-electrons.com/source/include/linux/mm_types.h?v=4.9#L32 | pagină fizică]], offset-ul în pagină și dimensiunea buferului. O pagină poate fi alocată folosind apelul [[http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.9#L484 | alloc_page]]. |
- | **Atenție** : Câmpul ''size'' al apelului [[http://lxr.free-electrons.com/source/fs/bio.c?v=3.13#L750 | bio_add_page]] trebuie să fie multiplu de dimensiunea sectorului dispozitivului. | + | <note important> |
- | + | Câmpul ''size'' al apelului [[http://lxr.free-electrons.com/source/block/bio.c?v=4.9#L799 | bio_add_page]] trebuie să fie multiplu de dimensiunea sectorului dispozitivului. | |
- | O pagină poate fi alocată folosind apelul [[http://lxr.free-electrons.com/source/include/linux/gfp.h?v=3.13#L345 | alloc_page]]. | + | </note> |
==== Folosirea conținutului unei structuri bio ==== | ==== Folosirea conținutului unei structuri bio ==== | ||
- | Pentru folosirea conținutului unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L46 | bio]], paginile de suport ale structurii trebuie mapate în spațiul de adresă nucleu de unde vor putea fi accesate. Pentru mapare/demapare se folosesc macrourile [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=3.13#L100 | __bio_kmap_atomic]] și [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=3.13#L104 | __bio_kunmap_atomic]] ((Mai multe informații despre maparea atomică a memoriei găsiți în [[http://lwn.net/images/pdf/LDD3/ch15.pdf | Linux Device Drivers 3rd Edition, Chapter 15. Memory Mapping and DMA - The Memory Map and Struct Page]])). | + | Pentru folosirea conținutului unei structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]], paginile de suport ale structurii trebuie mapate în spațiul de adresă nucleu de unde vor putea fi accesate. Pentru mapare/demapare se folosesc macrourile [[http://lxr.free-electrons.com/source/arch/x86/mm/highmem_32.c?v=4.9#L55 | kmap_atomic]] și [[http://lxr.free-electrons.com/source/include/linux/highmem.h?v=4.9#L124 | kunmap_atomic]] ((Mai multe informații despre maparea atomică a memoriei găsiți în [[http://lwn.net/images/pdf/LDD3/ch15.pdf | Linux Device Drivers 3rd Edition, Chapter 15. Memory Mapping and DMA - The Memory Map and Struct Page]])). |
Un exemplu tipic de folosire este: | Un exemplu tipic de folosire este: | ||
Line 507: | Line 504: | ||
static int my_xfer_bio(struct my_block_dev *dev, struct bio *bio) | static int my_xfer_bio(struct my_block_dev *dev, struct bio *bio) | ||
{ | { | ||
- | int i; | + | struct bio_vec bvec; |
- | struct bio_vec *bvec; | + | struct bvec_iter i; |
- | size_t start = bio->bi_sector * KERNEL_SECTOR_SIZE; | + | int dir = bio_data_dir(bio); |
- | /* Do each segment independently. */ | + | /* Do each segment independently. */ |
- | bio_for_each_segment(bvec, bio, i) { | + | bio_for_each_segment(bvec, bio, i) { |
- | char *buffer = __bio_kmap_atomic(bio, i); | + | sector_t sector = i.bi_sector; |
+ | char *buffer = kmap_atomic(bvec.bv_page); | ||
+ | unsigned long offset = bvec.bv_offset; | ||
+ | size_t len = bvec.bv_len; | ||
+ | |||
+ | /* process mapped buffer */ | ||
+ | my_block_transfer(dev, sector, len, buffer + offset, dir); | ||
- | /* process mapped buffer */ | + | kunmap_atomic(buffer); |
- | my_block_transfer(dev, start, bio_cur_bytes(bio), buffer, bio_data_dir(bio) == WRITE); | + | } |
- | + | ||
- | start += bio_cur_bytes(bio); | + | |
- | __bio_kunmap_atomic(buffer); | + | |
- | } | + | |
return 0; | return 0; | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | După cum se observă din exemplul de mai sus, parcurgerea unui ''bio'' presupune iterarea prin toate segmentele acestuia. Un segment ([[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L29|bio_vec]]) este definit de pagina adresei fizice, offset-ul în pagină și dimensiunea acestuia. | ||
+ | |||
+ | Pentru a simplifica procesarea unui bio se folosește macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L184|bio_for_each_segment]]. Aceasta va itera prin toate segmentele și de asemenea va actualiza informații globale, stocate într-un iterator ([[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L35|bvec_iter]]), cum ar fi sectorul curent precum și alte informații interne (indexul în vectorul de segmente, numărul de octeți rămași de procesat, etc.). | ||
Se pot stoca informații în buffer-ul mapat sau se pot extrage informații. | Se pot stoca informații în buffer-ul mapat sau se pot extrage informații. | ||
- | Următoarele câmpuri ale structurii ''bio'' sunt accesate fie direct, fie cu ajutorul unor macrodefiniții: | + | În cazul în care se folosesc cozile de cereri și se dorește prelucrarea cererilor la nivel de structură ''bio'', se va folosi macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L754 | rq_for_each_segment]] în locul [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=4.9#L184 | bio_for_each_segment]]. Această macrodefiniție parcurge fiecare segment din fiecare structură ''bio'' a unei cereri ''struct request'' și actualizează o structură [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L742 | struct req_iterator]]. Structura [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L742 | struct req_iterator]] conține structura curentă ''bio'' și iteratorul ce parcurge segmentele acestuia. Un exemplu tipic de folosire este: |
- | *''bi_sector'', primul sector de transferat, exprimat în sectoare de 512 octeți | + | |
- | *[[http://lxr.free-electrons.com/source/include/linux/bio.h?v=3.13#L69 | bio_sectors]], pentru obținerea numărului total de sectoare de transferat din cererea ''bio'' curentă | + | |
- | *[[http://lxr.free-electrons.com/source/include/linux/bio.h?v=3.13#L72 | bio_cur_bytes]], pentru obținerea numărului de octeți de transferat din pagina curentă | + | |
- | *[[http://lxr.free-electrons.com/source/include/linux/fs.h?v=3.13#L2237 | bio_data_dir]], pentru a determina dacă este o cerere de citire (''0'') sau de scriere (''1'') | + | |
- | *[[http://lxr.free-electrons.com/source/include/linux/bio.h?v=3.13#L138 | bio_for_each_segment]], pentru a parcurge vectorul de structuri [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=3.13#L25 | struct bio_vec]] ce conține paginile de memorie fizică dintr-o structură ''bio''. | + | |
- | În cazul în care se folosesc cozile de cereri și se dorește prelucrarea cererilor la nivel de structură ''bio'', se va folosi macrodefiniția [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=3.13#L749 | rq_for_each_segment]] în locul [[http://lxr.free-electrons.com/source/include/linux/bio.h?v=3.13#L138 | bio_for_each_segment]]. Această macrodefiniție parcurge fiecare segment din fiecare structură ''bio'' a unei cereri ''struct request'' și actualizează o structură [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=3.13#L737 | struct req_iterator]]. Structura [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=3.13#L737 | struct req_iterator]] conține structura curentă ''bio'' și indicele segmentului curent. | + | <code c> |
- | ==== Eliberarea unei structuri bio ==== | + | struct bio_vec bvec; |
+ | struct req_iterator iter; | ||
+ | |||
+ | rq_for_each_segment(bvec, req, iter) { | ||
+ | sector_t sector = iter.iter.bi_sector; | ||
+ | char *buffer = kmap_atomic(bvec.bv_page); | ||
+ | unsigned long offset = bvec.bv_offset; | ||
+ | size_t len = bvec.bv_len; | ||
+ | int dir = bio_data_dir(iter.bio); | ||
- | După ce un subsistem al nucleului folosește o structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L46 | bio]] va trebui să elibereze referința către aceasta. Acest lucru se realizează cu ajutorul funcției [[http://lxr.free-electrons.com/source/fs/bio.c?v=3.13#L496 | bio_put]]. Funcția [[http://lxr.free-electrons.com/source/fs/bio.c?v=3.13#L496 | bio_put]] este apelată și în exemplul anterior din [[http://lxr.free-electrons.com/source/drivers/md/md.c?v=3.13#L779 | drivers/md/md.c]]. | + | my_block_transfer(dev, sector, len, buffer + offset, dir); |
+ | |||
+ | kunmap_atomic(buffer); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== Eliberarea unei structuri bio ==== | ||
+ | După ce un subsistem al nucleului folosește o structură [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L26 | bio]] va trebui să elibereze referința către aceasta. Acest lucru se realizează cu ajutorul funcției [[http://lxr.free-electrons.com/source/block/bio.c?v=4.9#L529 | bio_put]]. | ||
==== Configurarea unei cozi de cerere la nivel de bio ==== | ==== Configurarea unei cozi de cerere la nivel de bio ==== | ||
- | Cu ajutorul funcției [[http://lxr.free-electrons.com/source/block/blk-core.c?v=3.13#L666 | blk_init_queue]] se putea specifica o funcție care să fie folosită pentru prelucrarea cererilor transmise driverului. Funcția primea ca argument coada de cereri și realiza prelucrări la nivel de structuri [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=3.13#L97|request]]. | + | Cu ajutorul funcției [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L781 | blk_init_queue]] se putea specifica o funcție care să fie folosită pentru prelucrarea cererilor transmise driverului. Funcția primea ca argument coada de cereri și realiza prelucrări la nivel de structuri [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L88|request]]. |
- | Dacă, din motive de flexibilitate, se dorește specificarea unei funcții care să realizeze prelucrări la nivel de [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L46 | bio]], trebuie folosită funcția [[http://lxr.free-electrons.com/source/block/blk-settings.c?v=3.13#L175 | blk_queue_make_request]] în conjuncție cu funcția [[http://lxr.free-electrons.com/source/block/blk-core.c?v=3.13#L546|blk_alloc_queue]]. Mai jos este prezentat un exemplu tipic de inițializare a unei funcții de prelucrare la nivel de [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L46 | bio]]: | + | Dacă, din motive de flexibilitate, se dorește specificarea unei funcții care să realizeze prelucrări la nivel de [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]], trebuie folosită funcția [[http://lxr.free-electrons.com/source/block/blk-settings.c?v=4.9#L136 | blk_queue_make_request]] în conjuncție cu funcția [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L645| blk_alloc_queue]]. Mai jos este prezentat un exemplu tipic de inițializare a unei funcții de prelucrare la nivel de [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]]: |
<code c> | <code c> | ||
Line 562: | Line 577: | ||
</code> | </code> | ||
- | Funcția ''my_make_request'' este de tipul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=3.13#L227 | make_request_fn]]. Un exemplu de folosire a unei astfel de funcții se găsește în [[http://lxr.free-electrons.com/source/drivers/md/raid1.c#L1047 | drivers/md/raid1.c]]. | + | Funcția ''my_make_request'' este de tipul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L225 | make_request_fn]]. Un exemplu de folosire a unei astfel de funcții se găsește în [[http://lxr.free-electrons.com/source/drivers/md/md.c?v=4.9#L4987 | drivers/md/md.c]]. |
- | În cazul în care se folosește această metodă, nu se mai folosesc cozile de cereri, fiecare cerere fiind reprezentă de o structură ''bio''. Astfel, transferul de date se reduce la parcurgerea fiecărei structuri ''bio'' după cum a fost prezentat [[#Folosirea_con.C8.9Binutului_unei_structurii_bio|mai sus]] și semnalarea terminării prelucrării acesteia cu ajutorul funcției [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L71 | bio_endio]]. | + | În cazul în care se folosește această metodă, nu se mai folosesc cozile de cereri, fiecare cerere fiind reprezentă de o structură ''bio''. Astfel, transferul de date se reduce la parcurgerea fiecărei structuri ''bio'' după cum a fost prezentat [[#Folosirea_con.C8.9Binutului_unei_structurii_bio|mai sus]] și semnalarea terminării prelucrării acesteia cu ajutorul funcției [[http://lxr.free-electrons.com/source/block/bio.c?v=4.9#L1734 | bio_endio]]. |
===== Resurse utile ===== | ===== Resurse utile ===== |