Linux Device Driver Initialization

This article briefly speaks about introduction to Linux PCI device driver initialization and its probing technique.

Introduction:

In Linux 3.5, the PCI device driver during the boot process is deployed in the form of statically linked or LKM typically loaded into the kernel as a kernel module. When the module is registered with the kernel it executes a callback function called xyz_init_module. The drivers probe function xyz_probe() is called indirectly when the drivers xyz_int_module() function registers with the PCI bus provided required conditions are met through kernel core layers and when PCI bus is configured successfully.

The PCI initialization framework must adhere three logical sections mentioned below:

  • The device driver should be capable of module initialization xyz_init_module routine
  • Secondly, capable of device initialization xyz_probe routine. Also referred as driver probe method.
  • Third,  module initialization routine should call pci_register_driver() in order to register pci driver to PCI core.

Note: I am referring to Intel 10 GigaBit Ethernet (drivers/net/ethernet/intel/ixgbe/) driver. All functions that used to discuss are from ixgbe_main.c. For instance xyz string replaced with ixgbe in module and device initialization routines are ixgbe_init_module() and ixgbe_probe() respectively.

Driver Register:

The ixgbe drivers ixgbe_init_module() function calls pci_register_driver(struct pci_driver *drv) by passing reference to structure of type pci_driver. struct pci_driver is an important structure all PCI drivers should have, which gets initilized with variables like drivers name, table list of PCI devices the driver can support, callback routines for the PCI core subsystem.

pci_driver structure created for each PCI device:

struct pci_driver {
struct list_head node;
const char *name;
const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
};

The drivers pci_driver structure has important member fields listed below:

  • name – Name to the driver which is unique among all PCI drivers in the kernel. It will appear under /sys/bus/pci/drivers.
  • pci_device_id – A table of device identification data consists type of chips this driver supports.
  • probe – The address of ixgbe_probe() function.
  • remove/suspend/resume/shutdown – address to the function that the PCI core system calls when PCI device is removed/suspended/resumed/shutdown respectively. Generally used by upper layers for power management.

pci_driver structure for ixgbe driver created by initializing fields as below:

static struct pci_driver ixgbe_driver = {
.name = ixgbe_driver_name,
.id_table = ixgbe_pci_tbl,
.probe = ixgbe_probe,
.remove = __devexit_p(ixgbe_remove),
#ifdef CONFIG_PM
.suspend = ixgbe_suspend,
.resume = ixgbe_resume,
#endif
.shutdown = ixgbe_shutdown,
.err_handler = &ixgbe_err_handler
};

Hence to register ixgbe’s struct pci_driver with PCI layer, call to pci_register_driver(&ixgbe_driver) initiates probing for the device in the underlying PCI core. The function returns value 0 if success and negative number on failure.

Probing:

Now lets understand how probing of ixgbe driver takes place through sequence of function calls registering with PCI core, when module initialization routine ixgbe_init_module() called for the first time when ixgbe driver is loaded.

  • ixgbe_init_module() calls pci_register_driver(&ixgbe_driver)
  • pci_register_driver() calls __pci_register_driver()
  • __pci_register_driver() binds the driver to PCI bus pci_bus_type
  • __pci_register_driver() calls driver_register()
  • driver_register() calls driver_find() to check if the driver is already registered.
  • If drivers registration is for first time, driver_register() calls bus_add_driver()
  • bus_add_driver() calls driver_attach()
  • driver_attach() function tries to bind the driver to device. It calls bus_for_each_device() which invokes callback for each device providing the __driver_attach() function as a callback.
  • __driver_attach() calls driver_probe_device() only if the device doesn’t have a driver yet.
  • driver_probe_device() calls really_probe()
  • really_probe() calls the probe method of PCI bus object being the method called pci_device_probe()
  • pci_device_probe() calls __pci_device_probe()
  • __pci_device_probe() checks for a PCI device for this driver, calls pci_call_probe()
  • pci_call_probe() calls local_pci_probe()
  • local_pci_probe() calls probe method ixgbe_probe() for ixgbe.

Note: The above call graph doesn’t could not be the same when any PCI device is not plugged.

Conclusions:

I’ve presented both abstract and concrete view on Linux PCI network device driver initialization and its method of probing considering Intel’s ixgbe driver. We’ve gone through how PCI device is registered right form module initilization till device initilization. In particular the device probing call trace is described when a PCI device is deployed in the form of loadable kernel module. In next article we will cover what exactly driver probe function setups in order to achieve network packet processing.

The above provided hyper-link’s are based on Linux 3.5

 References:

  1. Chapter 12, LDD3 
  2. Chapter 14, LDD3
  3. Linux PCI Init
Advertisements
This entry was posted in Device Driver, Linux and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s