This shows you the differences between two versions of the page.
so2:laboratoare:lab07:exercitii [2018/04/09 17:29] ionel.ghita |
so2:laboratoare:lab07:exercitii [2018/04/12 11:40] (current) elena.sandulescu [6. [2p] Prelucrarea cererilor din coada la nivel de bio] |
||
---|---|---|---|
Line 103: | Line 103: | ||
===== [10.5p] Exerciții ===== | ===== [10.5p] Exerciții ===== | ||
+ | |||
+ | <note important> | ||
+ | Înainte de începerea rezolvării laboratorului, rulați comanda ''%%git pull --rebase%%'' in directorul ''~/so2/linux'', pentru a obține ultima versiune a scheletului de laborator. | ||
+ | </note> | ||
==== 1. [1p] Dispozitiv de tip bloc ==== | ==== 1. [1p] Dispozitiv de tip bloc ==== | ||
- | Creați un modul de kernel care să permită înregistrarea, respectiv deînregistrarea unui dispozitiv de tip bloc. Porniți de la fișierele din directorul ''1-2-3-6-ram-disk/kernel'' din arhiva de sarcini a laboratorului. | + | Creați un modul de kernel care să permită înregistrarea, respectiv deînregistrarea unui dispozitiv de tip bloc. Porniți de la fișierele din directorul ''1-2-3-6-ram-disk/kernel'' din scheletul laboratorului. |
Urmăriți secțiunile marcate cu ''TODO 1'' în scheletul de laborator. Folosiți macrodefinițiile existente (''MY_BLOCK_MAJOR'', ''MY_BLKDEV_NAME''). Verificați valoarea întoarsă de funcția de înregistrare și, în caz de eroare, întoarceți cod de eroare. | Urmăriți secțiunile marcate cu ''TODO 1'' în scheletul de laborator. Folosiți macrodefinițiile existente (''MY_BLOCK_MAJOR'', ''MY_BLKDEV_NAME''). Verificați valoarea întoarsă de funcția de înregistrare și, în caz de eroare, întoarceți cod de eroare. | ||
Line 118: | Line 122: | ||
</note> | </note> | ||
- | Schimbați valoarea macroului ''MY_BLOCK_MAJOR'' la valoarea ''7''. Compilați modulul, copiați-l pe mașina virtuală și încărcați-l în nucleu. Observați că încărcarea eșuează întrucât există deja un alt driver/dispozitiv care are majorul ''7'' înregistrat în nucleu. | + | Schimbați valoarea macroului ''MY_BLOCK_MAJOR'' la valoarea ''254''. Compilați modulul, copiați-l pe mașina virtuală și încărcați-l în nucleu. Observați că încărcarea eșuează întrucât există deja un alt driver/dispozitiv care are majorul ''254'' înregistrat în nucleu. |
Restaurați valoarea ''240'' pentru macroul ''MY_BLOCK_MAJOR''. | Restaurați valoarea ''240'' pentru macroul ''MY_BLOCK_MAJOR''. | ||
- | ==== 2. [1.5p] Înregistrare disc ==== | + | ==== 2. [2p] Înregistrare disc ==== |
Modificați modulul anterior pentru a adăuga un disc asociat driverului. Analizați macrodefinițiile, structura ''struct my_block_dev'' și funcțiile existente din fișierul ''ram-disk.c''. | Modificați modulul anterior pentru a adăuga un disc asociat driverului. Analizați macrodefinițiile, structura ''struct my_block_dev'' și funcțiile existente din fișierul ''ram-disk.c''. | ||
Line 139: | Line 143: | ||
</note> | </note> | ||
- | Folosiți [[http://lxr.free-electrons.com/source/block/blk-core.c?v=4.9#L2879|__blk_end_request_all]] pentru încheierea prelucrării cererii. | + | Folosiți [[https://elixir.bootlin.com/linux/v4.15/source/block/blk-core.c#L3206|__blk_end_request_all]] pentru încheierea prelucrării cererii. |
Inserați modulul în kernel. Folosiți ''dmesg'' pentru a observa un mesaj transmis de modul. În momentul adăugării dispozitivului se transmite o cerere către acesta. Verificați prezența ''/dev/myblock'' și dacă nu există creați dispozitivul folosind comanda<code> | Inserați modulul în kernel. Folosiți ''dmesg'' pentru a observa un mesaj transmis de modul. În momentul adăugării dispozitivului se transmite o cerere către acesta. Verificați prezența ''/dev/myblock'' și dacă nu există creați dispozitivul folosind comanda<code> | ||
Line 154: | Line 158: | ||
Modificați modulul anterior pentru a crea un RAM disc: cererile către dispozitiv vor rezulta în citiri/scrieri într-o zonă de memorie. | Modificați modulul anterior pentru a crea un RAM disc: cererile către dispozitiv vor rezulta în citiri/scrieri într-o zonă de memorie. | ||
- | Zona de memorie aferentă ''%%dev->data%%'' este deja alocată în codul sursă din modul folosind [[http://lxr.free-electrons.com/source/mm/vmalloc.c?v=4.9#L1752 | vmalloc]]((Pentru simplitate, se folosește funcția vmalloc pentru alocarea vectorului în memorie; funcția vmalloc alocă o zonă de memorie contiguă în spațiul de adrese virtuale, iar funcția vfree o dealocă. Mai multe despre alocarea memoriei în kernel găsiți în [[http://lwn.net/images/pdf/LDD3/ch08.pdf | Linux Device Drivers 3rd Edition, Chapter 8. Allocating memory]])). Pentru dezalocare în modul se folosește [[http://lxr.free-electrons.com/source/mm/vmalloc.c?v=4.9#L1514 | vfree]]. | + | Zona de memorie aferentă ''%%dev->data%%'' este deja alocată în codul sursă din modul folosind [[https://elixir.bootlin.com/linux/v4.15/source/mm/vmalloc.c#L1829 | vmalloc]]((Pentru simplitate, se folosește funcția vmalloc pentru alocarea vectorului în memorie; funcția vmalloc alocă o zonă de memorie contiguă în spațiul de adrese virtuale, iar funcția vfree o dealocă. Mai multe despre alocarea memoriei în kernel găsiți în [[http://lwn.net/images/pdf/LDD3/ch08.pdf | Linux Device Drivers 3rd Edition, Chapter 8. Allocating memory]])). Pentru dezalocare în modul se folosește [[https://elixir.bootlin.com/linux/v4.15/source/mm/vmalloc.c#L1581 | vfree]]. |
<note> | <note> | ||
Line 163: | Line 167: | ||
<note tip> | <note tip> | ||
- | Pentru a afla dimensiunea datelor din request folosiți macro-ul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L865|blk_rq_cur_bytes]]. **Nu** folosiți macro-ul [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L860|blk_rq_bytes]]. | + | Pentru a afla dimensiunea datelor din request folosiți macro-ul [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blkdev.h#L1032|blk_rq_cur_bytes]]. **Nu** folosiți macro-ul [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blkdev.h#L1027|blk_rq_bytes]]. |
</note> | </note> | ||
Line 178: | Line 182: | ||
</note> | </note> | ||
- | Pentru testare folosiți fișierul de test ''ram-disk-test.c''. Îl compilați folosind, pe sistemul fizic, comanda<code> | + | Pentru testare folosiți fișierul de test ''user/ram-disk-test.c'', care se compilează automat la ''make build'', se copiază pe mașina virtuală la ''make copy'' și se rulează folosind, pe mașina virtuală QEMU, comanda<code> |
- | make -f Makefile.test | + | |
- | </code> | + | |
- | și apoi îl rulați folosind, pe mașina virtuală QEMU, comanda<code> | + | |
./ram-disk-test | ./ram-disk-test | ||
</code> | </code> | ||
Line 190: | Line 191: | ||
==== 4. [2p] Citirea datelor de pe disc ==== | ==== 4. [2p] Citirea datelor de pe disc ==== | ||
- | Scopul acestui exercițiu este să citiți datele de pe discul ''PHYSICAL_DISK_NAME'' direct din kernel. | + | Scopul acestui exercițiu este să citiți datele de pe discul ''PHYSICAL_DISK_NAME'' (''/dev/vdb'') direct din kernel. |
+ | |||
+ | <note important> | ||
+ | Înainte de rezolvarea exercițiului, este necesar să adaugăm discul la mașina virtuală. Pentru aceasta, generați un fișier pe care îl vom folosi ca imaginea discului folosind comanda <code>dd if=/dev/zero of=qemu/mydisk.img bs=1024 count=1</code> și adăugați argumentul ''-drive file=qemu/mydisk.img,if=virtio,format=raw'' comenzii ''qemu'', în fișierul ''qemu/Makefile'' (în variabila ''QEMU_OPTS'') | ||
+ | </note> | ||
- | Urmăriți comentariile marcate cu ''TODO 4'' în directorul ''4-5-relay-disk/'' și completați funcțiile ''open_disk'' și ''close_disk''. Folosiți funcțiile [[http://lxr.free-electrons.com/source/fs/block_dev.c?v=4.9#L1467 | blkdev_get_by_path]] și [[http://lxr.free-electrons.com/source/fs/block_dev.c?v=4.9#L1619 | blkdev_put]]. Dispozitivul trebuie deschis în mod read-write exclusiv (''FMODE_READ | FMODE_WRITE | FMODE_EXCL''), iar ca holder trebuie să folosiți modulul curent (''THIS_MODULE''). | + | Urmăriți comentariile marcate cu ''TODO 4'' în directorul ''4-5-relay-disk/'' și completați funcțiile ''open_disk'' și ''close_disk''. Folosiți funcțiile [[https://elixir.bootlin.com/linux/v4.15/source/fs/block_dev.c#L1642 | blkdev_get_by_path]] și [[https://elixir.bootlin.com/linux/v4.15/source/fs/block_dev.c#L1752 | blkdev_put]]. Dispozitivul trebuie deschis în mod read-write exclusiv (''FMODE_READ | FMODE_WRITE | FMODE_EXCL''), iar ca holder trebuie să folosiți modulul curent (''THIS_MODULE''). |
- | Completați funcția ''send_test_bio''. Va trebui să creați un nou bio pe care să-l completați, să-l submiteți și să-l așteptați. Citiți primul sector al discului. Pentru așteptare folosiți [[http://lxr.free-electrons.com/source/block/bio.c?v=4.9#L863|submit_bio_wait]]. | + | Completați funcția ''send_test_bio''. Va trebui să creați un nou bio pe care să-l completați, să-l submiteți și să-l așteptați. Citiți primul sector al discului. Pentru așteptare folosiți [[https://elixir.bootlin.com/linux/v4.15/source/block/bio.c#L928|submit_bio_wait]]. |
<note tip> | <note tip> | ||
- | Primul sector al discului este sectorul cu indexul ''0''. La această valoare trebuie inițializat câmpul ''bi_iter.bi_sector'' al structurii [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25|bio]]. | + | Primul sector al discului este sectorul cu indexul ''0''. La această valoare trebuie inițializat câmpul ''bi_iter.bi_sector'' al structurii [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blk_types.h#L50|bio]]. |
- | Pentru operația de citire folosiți macro-urile ''REQ_OP_READ'' și [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L95|bio_set_op_attrs]]. | + | Pentru operația de citire folosiți macro-urile ''REQ_OP_READ'' și [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blk_types.h#L271|bio_set_op_attrs]]. |
</note> | </note> | ||
- | După terminarea operației afișați primii 3 octeți din datele citite de bio. Folosiți formatul ''%%"%02x"%%'' la ''printk'' pentru afișarea datelor și macrourile [[http://lxr.free-electrons.com/source/arch/x86/mm/highmem_32.c?v=4.9#L55 | kmap_atomic]], respectiv [[http://lxr.free-electrons.com/source/include/linux/highmem.h?v=4.9#L124 | kunmap_atomic]]. | + | După terminarea operației afișați primii 3 octeți din datele citite de bio. Folosiți formatul ''%%"%02x"%%'' la ''printk'' pentru afișarea datelor și macrourile [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/mm/highmem_32.c#L55 | kmap_atomic]], respectiv [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/highmem.h#L125 | kunmap_atomic]]. |
<note tip> | <note tip> | ||
- | Ca argument pentru [[http://lxr.free-electrons.com/source/arch/x86/mm/highmem_32.c?v=4.9#L55 | kmap_atomic]] folosiți chiar pagina alocată mai sus în cod în cadrul variabilei ''page''. | + | Ca argument pentru [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/mm/highmem_32.c#L55 | kmap_atomic]] folosiți chiar pagina alocată mai sus în cod în cadrul variabilei ''page''. |
</note> | </note> | ||
Line 213: | Line 218: | ||
</note> | </note> | ||
- | Pentru testare folosiți scriptul ''test-relay-disk''. Nu este nevoie să încărcați modulul în nucleu, va fi încărcat de test. Pentru a rula scriptul folosiți comanda<code> | + | Pentru testare folosiți scriptul ''test-relay-disk'', care este copiat pe mașina virtuală la ''make copy''. Dacă nu este copiat, asigurați-vă că este executabil (''chmod +x test-relay-disk''). Nu este nevoie să încărcați modulul în nucleu, va fi încărcat de test. Pentru a rula scriptul folosiți comanda<code> |
./test-relay-disk | ./test-relay-disk | ||
</code> | </code> | ||
Scriptul scrie ''%%"abc"%%'' la începutul discului indicat de ''PHYSICAL_DISK_NAME''. În urma rulării, modulul va afișa ''61 62 63'' (valorile hexazecimale corespunzătoare). | Scriptul scrie ''%%"abc"%%'' la începutul discului indicat de ''PHYSICAL_DISK_NAME''. În urma rulării, modulul va afișa ''61 62 63'' (valorile hexazecimale corespunzătoare). | ||
- | ==== 5. [2p] Scrierea unui mesaj pe disc ==== | + | ==== 5. [1.5p] Scrierea unui mesaj pe disc ==== |
Urmăriți comentariile marcate cu ''TODO 5'' pentru scrierea unui mesaj (''BIO_WRITE_MESSAGE'') pe disc. | Urmăriți comentariile marcate cu ''TODO 5'' pentru scrierea unui mesaj (''BIO_WRITE_MESSAGE'') pe disc. | ||
- | Trebuie să actualizați funcția ''send_test_bio'' pentru a primi ca argument tipul operației (citire sau scriere). Apelați în ''relay_init'' funcția pentru citire iar în ''relay_exit'' funcția pentru scriere. Recomandăm folosirea macro-urilor ''REQ_OP_READ'' și ''REQ_OP_WRITE''. | + | Funcția ''send_test_bio'' primește ca argument tipul operației (citire sau scriere). Apelați în ''relay_init'' funcția pentru citire iar în ''relay_exit'' funcția pentru scriere. Recomandăm folosirea macro-urilor ''REQ_OP_READ'' și ''REQ_OP_WRITE''. |
- | În cadrul funcției ''send_test_bio'', dacă operația este de scriere, completați buffer-ul aferent bio-ului cu mesajul ''BIO_WRITE_MESSAGE''. Folosiți macrourile [[http://lxr.free-electrons.com/source/arch/x86/mm/highmem_32.c?v=4.9#L55 | kmap_atomic]], respectiv [[http://lxr.free-electrons.com/source/include/linux/highmem.h?v=4.9#L124 | kunmap_atomic]] pentru lucrul cu buffer-ul aferent bio-ului. | + | În cadrul funcției ''send_test_bio'', dacă operația este de scriere, completați buffer-ul aferent bio-ului cu mesajul ''BIO_WRITE_MESSAGE''. Folosiți macrourile [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/mm/highmem_32.c#L55 | kmap_atomic]], respectiv [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/highmem.h#L125 | kunmap_atomic]] pentru lucrul cu buffer-ul aferent bio-ului. |
<note tip> | <note tip> | ||
- | Trebuie să actualizați tipul operației [[http://lxr.free-electrons.com/source/include/linux/blk_types.h#L46|structurii bio]] folosind macrodefiniția ''bio_set_op_attrs'' | + | Trebuie să actualizați tipul operației [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blk_types.h#L50|structurii bio]] folosind macrodefiniția ''bio_set_op_attrs'' |
</note> | </note> | ||
Line 243: | Line 248: | ||
Configurați macro-ul ''USE_BIO_TRANSFER'' la valoarea 1. | Configurați macro-ul ''USE_BIO_TRANSFER'' la valoarea 1. | ||
- | Implementați funcția ''my_xfer_request''. Folosiți [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L754|rq_for_each_segment]] pentru a parcurge structurile [[http://lxr.free-electrons.com/source/include/linux/bvec.h?v=4.9#L29 | bio_vec]] ale fiecărui [[http://lxr.free-electrons.com/source/include/linux/blk_types.h?v=4.9#L25 | bio]] din [[http://lxr.free-electrons.com/source/include/linux/blkdev.h?v=4.9#L88|request]]. | + | Implementați funcția ''my_xfer_request''. Folosiți [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blkdev.h#L927|rq_for_each_segment]] pentru a parcurge structurile [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/bvec.h#L30 | bio_vec]] ale fiecărui [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blk_types.h#L50 | bio]] din [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/blkdev.h#L135|request]]. |
<note tip> | <note tip> | ||
Line 258: | Line 263: | ||
<note tip> | <note tip> | ||
- | Folosiți macro-ul [[http://lxr.free-electrons.com/source/include/linux/fs.h?v=4.9#L2510|bio_data_dir]] pentru a afla direcția de citire sau scriere pentru un bio. | + | Folosiți macro-ul [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/bio.h#L76|bio_data_dir]] pentru a afla direcția de citire sau scriere pentru un bio. |
</note> | </note> | ||
- | Folosiți macrourile [[http://lxr.free-electrons.com/source/arch/x86/mm/highmem_32.c?v=4.9#L55|kmap_atomic]], respectiv [[http://lxr.free-electrons.com/source/include/linux/highmem.h?v=4.9#L120|kunmap_atomic]] pentru maparea paginilor fiecărui bio și accesarea bufferelor acestuia. Pentru transferul efectiv, apelați funcția ''my_block_transfer'' implementată la exercițiul anterior. | + | Folosiți macrourile [[https://elixir.bootlin.com/linux/v4.15/source/arch/x86/mm/highmem_32.c#L55|kmap_atomic]], respectiv [[https://elixir.bootlin.com/linux/v4.15/source/include/linux/highmem.h#L125|kunmap_atomic]] pentru maparea paginilor fiecărui bio și accesarea bufferelor acestuia. Pentru transferul efectiv, apelați funcția ''my_block_transfer'' implementată la exercițiul anterior. |
- | Pentru testare folosiți fișierul de test ''ram-disk-test.c''. Îl compilați folosind, pe sistemul fizic, comanda<code> | + | Pentru testare folosiți fișierul de test ''ram-disk-test.c''. |
- | make -f Makefile.test | + | |
</code> | </code> | ||
- | și apoi îl rulați folosind, pe mașina virtuală QEMU, comanda<code> | + | îl rulați folosind, pe mașina virtuală QEMU, comanda<code> |
./ram-disk-test | ./ram-disk-test | ||
</code> | </code> |