e100 - driver de rețea

  • Termen de predare: Miercuri, 23 Mai 2018, ora 23:00

Scopul temei

  • implementarea unui driver de rețea
  • citire documentație hardware, implementare conform specificațiilor
  • familiarizare cu următoarele concepte: întreruperi, transfer DMA, I/O ports
  • citire surse, kernel hacking

Enunț

Să se scrie un driver pentru placa de rețea Intel 82559ER (device întâlnit şi sub numele de e100 sau eepro100). Modulul va trebui să suporte device-urile de rețea configurate în maşina virtuală qemu. Specificațiile se gasesc in documentul:

Nu va trebui să ciți în întregime documentul de mai sus, ci doar secțiunile indicate în enunț.

Puteți porni implementarea de la zero sau folosi scheletul de cod generat:

    $ LABS=assignments/6-e100 make skels 

Precizări

  • modulul de kernel se va numi e100-ix.ko
  • interfețele de rețea gestionate de acesta se vor numi ixethID, unde numerotarea pentru ID începe de la 0 (astfel, prima interfață va fi ixeth0, următoarea ixeth1 ș.a.m.d)
  • device-ul e100 se găseşte pe magistrala PCI, va trebui descoperit şi gestionat folosind o structură de tipul pci_driver
  • driverul va fi accesat ca un device de tip rețea, structura centrală pe care trebuie să o urmăriți este struct net_device
  • recepționarea pachetelor se va face în întrerupere, nu se va folosi polling sau NAPI

Interfața PCI

Urmăriți comentariile marcate cu TODO 1:

  • declarați o structură de tipul struct pci_driver şi inițializați următoarele câmpuri
    • .name - numele driverului, acesta va fi inițializat cu valoarea e100-ix
    • .id_table - structură care identifică unic deviceul e100 în sistem. Vendor ID şi device ID vor fi găsite în secțiunea 4 din manualul dezvoltatorului.
    • .probe - funcție apelată de subsystemul PCI din kernel atunci când în urma parcurgerii device-urilor din sistem se va găsi un device cu identitatea dată de .id_table.
    • .remove - funcție apelată atunci când driver-ul sau device-ul sunt scoase din sistem.
  • în funcția de init a modulului înregistrați structura pci_driver. Operația aceasta va avea ca efect apelarea funcției probe
  • în funcția de exit a modului deînregistrați structura pci_driver. Operația aceasta va avea ca efect apelarea funcției remove

În acest moment puteți testa daca device-ul de rețea este enumerat pe magistrala de PCI (observați dacă se apelează funcția probe).

Magistrala PCI conține linii de date, adrese și întreruperi și permite procesorului să comunice cu device-urile conectate. După ce un device este descoperit (enumerat) pe magistrala PCI este nevoie să fie inițializat. În procesul de inițializare vom aloca spațiul de I/O și memorie și vom obține linia de întrerupere folosită.

Urmăriți comentariile marcate cu TODO 2:

  • inițializați device-ul PCI folosind funcția pci_enable_device (activeaza I/O și memoria și trezește device-ul dacă era suspendat)
  • rezervați zonele de memorie si I/O pentru device-ul PCI folosind funcția pci_request_regions
  • device-ul e100 suporta urmatoarele zone (chapter 4.1.10)
    • CSR Memory Mapped Base Address Register (BAR 0 at offset 10)
    • CSR I/O Mapped Base Address Register (BAR 1 at offset 14)
  • verificați dacă device-ul suporta DMA folosind funcția pci_set_dma_mask
  • activați DMA apelând funcția pci_set_master

e100 private data

Structura e100_priv_data va contine informatii specifice fiecarui device de retea gestionat de driver. În primul rând va conține informații despre pci_dev și netdevice-ul curent, apoi informatii de stare, registre, unitatea de transmisie, unitatea de recepție, etc. Spațiul de memorie pentru e100_priv_data se va aloca în funcția alloc_etherdev urmăriți sectiunea structura_net_device.

Pentru a adauga câmpuri noi în structura e100_priv_data urmăriți TODO 3.

Structura net_device

Este reprezentarea din kernel a interfeței de rețea din user-space (ixeth0, ixeth1, ș.a.m.d)

Urmăriți comentariile marcate cu TODO 4:

  • structura net_device va fi alocată şi înregistrată (în funcția probe) atunci când se descoperă un device de rețea pe magistrala PCI
    • folosiți funcția alloc etherdev pentru a aloca memorie
    • va trebui să adăugați şi implementați operațiile ndo_open, ndo_stop şi ndo_start_xmit din câmpul netdev_ops
    • folosiți funcția register_netdev pentru înregistrare
  • structura net_device va fi deînregistrată şi eliberată (în funcția remove) atunci când device-ul sau modulul sunt înlăturate
    • folosiți funcția unregister_netdev pentru deînregistrare
    • folosiți funcția free_netdev pentru eliberare de memorie

Modelul de funcționare al plăcii de rețea e100

e100 conține două unități funcționale descrise in capitolul 6.5 din manualul dezvoltatorului. Aceste unități funcționează ca două procesoare independente și îndeplinesc următoarele roluri:

  • unitate de comanda (CU), este responsabilă pentru a executa comenzi primite de la driver. De exemplu, un driver poate trimite o comandă pentru a configura placa de rețea sau pentru a transmite un pachet.
  • unitatea de recepție (RU), este responsabilă pentru a primi pachete.

Driverul comunică cu unitățile CU și RU prin intermediul registrului CSR (Command Register) și prin intermediul DMA rings.

Transmiterea pachetelor

  • transmiterea pachetelor se va face în funcția .ndo_start_xmit
  • driverul va aloca o zonă de memorie DMA (DMA ring) şi va instrui placa de rețea să trimită pachetele din acea zonă.

Driverul trebuie sa construiasca un ring DMA in RAM pentru a instrui device-ul e100 unde se găsesc pachetele pe care dorește să le trimită. Secțiunea 6.4 din manual descrie formatul unui DMA ring.

Un ring DMA de control este alcătuit din buffere denumite Control Blocks (CB). Un CB generic arată astfel:

 +--------------+--------------+
 |    CONTROL   |    STATUS    |
 +--------------+--------------+
 |            LINK             |
 +--------------+--------------+
 |    COMMAND SPECIFIC DATA    |
 +--------------+--------------+

Mai multe detalii despre trimiterea pachetelor citiți in secțiunea Transmitting Packets de aici. Urmăriți comentariile marcate cu TODO 5.

Recepționarea pachetelor

  • recepționarea pachetelor se va face în handlerul de întrerupere
  • la primirea unui pachet device-ul e100 copiază datele într-o zonă DMA şi generează o întrerupere pentru a informa procesorul de sosirea unui nou pachet.

Un al doilea DMA ring este necesar pentru recepționarea pachetelor. Fiecare buffer din DMA ring-ul de recepție este denumit RFD (Receive Frame Descriptor).

Unitatea de recepție (RU) se ocupă de primirea pachetelor. La primirea unui pachet din rețea, copiaza pachetul în urmator-ul RFD liber, marchează RFD-ul valid și se mută la următorul RFD.

Mai multe detalii despre trimiterea pachetelor citiți in secțiunea Receiving Packets de aici. Urmăriți comentariile cu TODO 6.

e100 header

Antetul e100 conține descrierea in memorie a structurilor importante expuse de device-ul e100.

  • struct csr, Control/Status Register descris in secțiunea 6.3.1
  • struct tcb, Transmit Command Block descris in secțiunea 6.4.2.5
  • struct cb, descrie un Command Block generic.

Testare

Pentru simplificarea procesului de corectare a temelor, dar și pentru a reduce greșelile temelor trimise, corectarea temelor se va face automat cu ajutorul unor teste publice.

Sfaturi

Pentru a vă mări șansele de a obține nota maximă, citiți și respectați coding style-ul kernelului Linux descris din documentul Coding Style.

De asemenea, folosiți următoarele tool-uri de analiza statică pentru a verifica codul:

checkpatch.pl

  •  /path/to/linux-4.9.11/scripts/checkpatch.pl --no-tree --terse -f /path/to/your/src-file.c 

sparse

  •  sudo apt-get install sparse 
  •  cd /path/to/linux-4.9.11 
     make C=2 /path/to/your/src-file.c 

cppcheck

  •  sudo apt-get install cppcheck 
  •  cppcheck /path/to/your/src-file.c 

Depunctări

Tema valorează 2 puncte.

Depunctările generale pentru teme se găsesc pe pagina de Indicații generale.

În cazuri excepționale (tema trece testele prin nerespectarea cerințelor) și în cazul în care tema nu trece toate testele se poate scădea mai mult decât este menționat mai sus.

Submitere

Arhiva temei va fi submisă pe vmchecker, în conformitate cu precizările din pagina de reguli.

Din interfața vmchecker alegeți opțiunea e100 Driver, aferentă acestei teme.

Resurse

Resursele temei se găsesc și în repo-ul so2-assignments de pe GitHub. Repo-ul conține și un script Bash care vă ajută să vă creați un repository privat pe instanța de GitLab a facultății. Urmăriți indicațiile din README și de pe pagina de Wiki dedicată pentru git.

FAQ

  1. Unde setăm adresa MAC?
    • Adresa MAC trebuie pusă în câmpul dev_addr din structura net_device înaintea înregistrării netdevice-ului în kernel (register_netdevice) în funcția probe.
  2. Este necesar să citim adresa MAC din EEPROM-ul e100? Cum luăm adresa MAC ?
    • Nu este necesar să citiți adresa MAC din EEPROM. Puteți configura o adresă random folosind funcția eth_hw_addr_random, atenție apoi să trimiteți plăcii de rețea comanda de setare a noului mac.
    • Este acceptată și varianta mai simplă în care puteți folosi MAC-ul default al plăcii de rețea (52:54:00:12:34:56).

Întrebări

Pentru întrebări legate de temă puteți consulta arhivele listei de discuții sau puteți trimite un e-mail (trebuie să fiți înregistrați). Vă rugăm să urmăriți și să respectați indicațiile de utilizare a listei.

so2/teme/tema6.txt · Last modified: 2018/05/20 10:04 by alexandru.mocanu95
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0