Warning

In caso di dubbi sulla correttezza del contenuto di questa traduzione, l’unico riferimento valido è la documentazione ufficiale in inglese. Per maggiori informazioni consultate le avvertenze.

Original:

Symbol Namespaces

Translator:

Federico Vaga <federico.vaga@vaga.pv.it>

Spazio dei nomi dei simboli

Questo documento descrive come usare lo spazio dei nomi dei simboli per strutturare quello che viene esportato internamente al kernel grazie alle macro della famiglia EXPORT_SYMBOL().

1. Introduzione

Lo spazio dei nomi dei simboli è stato introdotto come mezzo per strutturare l’API esposta internamente al kernel. Permette ai manutentori di un sottosistema di organizzare i simboli esportati in diversi spazi di nomi. Questo meccanismo è utile per la documentazione (pensate ad esempio allo spazio dei nomi SUBSYSTEM_DEBUG) così come per limitare la disponibilità di un gruppo di simboli in altre parti del kernel. Ad oggi, i moduli che usano simboli esportati da uno spazio di nomi devono prima importare detto spazio. Altrimenti il kernel, a seconda della configurazione, potrebbe rifiutare di caricare il modulo o avvisare l’utente di un’importazione mancante.

2. Come definire uno spazio dei nomi dei simboli

I simboli possono essere esportati in spazi dei nomi usando diversi meccanismi. Tutti questi meccanismi cambiano il modo in cui EXPORT_SYMBOL e simili vengono guidati verso la creazione di voci in ksymtab.

2.1 Usare le macro EXPORT_SYMBOL

In aggiunta alle macro EXPORT_SYMBOL() e EXPORT_SYMBOL_GPL(), che permettono di esportare simboli del kernel nella rispettiva tabella, ci sono varianti che permettono di esportare simboli all’interno di uno spazio dei nomi: EXPORT_SYMBOL_NS() ed EXPORT_SYMBOL_NS_GPL(). Queste macro richiedono un argomento aggiuntivo: lo spazio dei nomi. Tenete presente che per via dell’espansione delle macro questo argomento deve essere un simbolo di preprocessore. Per esempio per esportare il simbolo usb_stor_suspend nello spazio dei nomi USB_STORAGE usate:

EXPORT_SYMBOL_NS(usb_stor_suspend, USB_STORAGE);

Di conseguenza, nella tabella dei simboli del kernel ci sarà una voce rappresentata dalla struttura kernel_symbol che avrà il campo namespace (spazio dei nomi) impostato. Un simbolo esportato senza uno spazio dei nomi avrà questo campo impostato a NULL. Non esiste uno spazio dei nomi di base. Il programma modpost e il codice in kernel/module/main.c usano lo spazio dei nomi, rispettivamente, durante la compilazione e durante il caricamento di un modulo.

2.2 Usare il simbolo di preprocessore DEFAULT_SYMBOL_NAMESPACE

Definire lo spazio dei nomi per tutti i simboli di un sottosistema può essere logorante e di difficile manutenzione. Perciò è stato fornito un simbolo di preprocessore di base (DEFAULT_SYMBOL_NAMESPACE), che, se impostato, diventa lo spazio dei simboli di base per tutti gli usi di EXPORT_SYMBOL() ed EXPORT_SYMBOL_GPL() che non specificano esplicitamente uno spazio dei nomi.

Ci sono molti modi per specificare questo simbolo di preprocessore e il loro uso dipende dalle preferenze del manutentore di un sottosistema. La prima possibilità è quella di definire il simbolo nel Makefile del sottosistema. Per esempio per esportare tutti i simboli definiti in usb-common nello spazio dei nomi USB_COMMON, si può aggiungere la seguente linea in drivers/usb/common/Makefile:

ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=USB_COMMON

Questo cambierà tutte le macro EXPORT_SYMBOL() ed EXPORT_SYMBOL_GPL(). Invece, un simbolo esportato con EXPORT_SYMBOL_NS() non verrà cambiato e il simbolo verrà esportato nello spazio dei nomi indicato.

Una seconda possibilità è quella di definire il simbolo di preprocessore direttamente nei file da compilare. L’esempio precedente diventerebbe:

#undef  DEFAULT_SYMBOL_NAMESPACE
#define DEFAULT_SYMBOL_NAMESPACE USB_COMMON

Questo va messo prima di un qualsiasi uso di EXPORT_SYMBOL.

3. Come usare i simboli esportati attraverso uno spazio dei nomi

Per usare i simboli esportati da uno spazio dei nomi, i moduli del kernel devono esplicitamente importare il relativo spazio dei nomi; altrimenti il kernel potrebbe rifiutarsi di caricare il modulo. Il codice del modulo deve usare la macro MODULE_IMPORT_NS per importare lo spazio dei nomi che contiene i simboli desiderati. Per esempio un modulo che usa il simbolo usb_stor_suspend deve importare lo spazio dei nomi USB_STORAGE usando la seguente dichiarazione:

MODULE_IMPORT_NS(USB_STORAGE);

Questo creerà un’etichetta modinfo per ogni spazio dei nomi importato. Un risvolto di questo fatto è che gli spazi dei nomi importati da un modulo possono essere ispezionati tramite modinfo:

$ modinfo drivers/usb/storage/ums-karma.ko
[...]
import_ns:      USB_STORAGE
[...]

Si consiglia di posizionare la dichiarazione MODULE_IMPORT_NS() vicino ai metadati del modulo come MODULE_AUTHOR() o MODULE_LICENSE(). Fate riferimento alla sezione 5. per creare automaticamente le importazioni mancanti.

4. Caricare moduli che usano simboli provenienti da spazi dei nomi

Quando un modulo viene caricato (per esempio usando insmod), il kernel verificherà la disponibilità di ogni simbolo usato e se lo spazio dei nomi che potrebbe contenerli è stato importato. Il comportamento di base del kernel è di rifiutarsi di caricare quei moduli che non importano tutti gli spazi dei nomi necessari. L’errore verrà annotato e il caricamento fallirà con l’errore EINVAL. Per caricare i moduli che non soddisfano questo requisito esiste un’opzione di configurazione: impostare MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y caricherà i moduli comunque ma emetterà un avviso.

5. Creare automaticamente la dichiarazione MODULE_IMPORT_NS

La mancanza di un’importazione può essere individuata facilmente al momento della compilazione. Infatti, modpost emetterà un avviso se il modulo usa un simbolo da uno spazio dei nomi che non è stato importato. La dichiarazione MODULE_IMPORT_NS() viene solitamente aggiunta in un posto ben definito (assieme agli altri metadati del modulo). Per facilitare la vita di chi scrive moduli (e i manutentori di sottosistemi), esistono uno script e un target make per correggere le importazioni mancanti. Questo può essere fatto con:

$ make nsdeps

Lo scenario tipico di chi scrive un modulo potrebbe essere:

- scrivere codice che dipende da un simbolo appartenente ad uno spazio
  dei nomi non importato
- eseguire ``make``
- aver notato un avviso da modpost che parla di un'importazione
  mancante
- eseguire ``make nsdeps`` per aggiungere import nel posto giusto

Per i manutentori di sottosistemi che vogliono aggiungere uno spazio dei nomi, l’approccio è simile. Di nuovo, eseguendo make nsdeps aggiungerà le importazioni mancanti nei moduli inclusi nel kernel:

- spostare o aggiungere simboli ad uno spazio dei nomi (per esempio
  usando EXPORT_SYMBOL_NS())
- eseguire ``make`` (preferibilmente con allmodconfig per coprire tutti
  i moduli del kernel)
- aver notato un avviso da modpost che parla di un'importazione
  mancante
- eseguire ``make nsdeps`` per aggiungere import nel posto giusto

Potete anche eseguire nsdeps per moduli esterni. Solitamente si usa così:

$ make -C <path_to_kernel_src> M=$PWD nsdeps