Chapter 14 The Linux Device Model CCU EECOMM

  • Slides: 36
Download presentation
Chapter 14 The Linux Device Model 潘仁義 CCU EE&COMM

Chapter 14 The Linux Device Model 潘仁義 CCU EE&COMM

The 2. 6 device model The model provides abstraction, which supports: Power management and

The 2. 6 device model The model provides abstraction, which supports: Power management and system shutdown Understanding of the system’s structure Right order to shutdown Communication with user space Sysfs Knobs for changing operating parameters Hot-pluggable devices Device classes Describe devices at a functional level Object lifecycles Reference count

Device model tree Sysfs (跑個tree /sys 吧? ) /proc, /dev, /sysfs Authors can ignore

Device model tree Sysfs (跑個tree /sys 吧? ) /proc, /dev, /sysfs Authors can ignore the model, and trust it Understanding device model is good, if struct leaks Ex. the generic DMA code works with struct device Advanced material that need not be read

Object oriented programming (插個花) Abstract Data typing Information hiding Encapsulation Inheritance Kobject, Kset Bus,

Object oriented programming (插個花) Abstract Data typing Information hiding Encapsulation Inheritance Kobject, Kset Bus, driver, device, partition… Derive more specialized classes from a common class Polymorphism Refers to the object's ability to respond in an individual manner to the same message hotplug(), match(), Dynamic binding probe(), kobj_type Refers to the mechanism that resolves a virtual function call at runtime You can derive modified action that override the old one even after the code is compiled.

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together

Kobject, Ksets, and Subsystems struct kobject supports Reference counting of objects Tracking the lifecycle

Kobject, Ksets, and Subsystems struct kobject supports Reference counting of objects Tracking the lifecycle Sysfs representation A visible representation Data structure glue Made up of multiple hierarchies with numerous links Hotplug event handling Notify user space about the comings and goings of hardware $(KERNELDIR)/lib/kobject*. c

Kobject basics (0/3) 1. struct kobject { 2. const char * k_name; 3. char

Kobject basics (0/3) 1. struct kobject { 2. const char * k_name; 3. char name[KOBJ_NAME_LEN]; 4. struct kref; 5. struct list_head entry; 6. struct kobject * parent; 7. struct kset * kset; 8. struct kobj_type * ktype; 9. struct dentry * dentry; 10. }; 11. struct kset { 12. struct subsystem * subsys; 13. struct kobj_type * ktype; 14. struct list_head list; 15. spinlock_t list_lock; 16. struct kobject kobj; 17. struct kset_hotplug_ops * hotplug_ops; 18. }; Directory entry, maybe for sysfs

Kobject basics (1/3) Embedded kobjects A common type embedded in other structures A top-level,

Kobject basics (1/3) Embedded kobjects A common type embedded in other structures A top-level, abstract class from which other classes are derived Ex. in ch 3, struct cdev { struct kobject kobj; struct module *owner; struct file_operations *ops; dev_t dev; }; struct kobject *kp = …; struct cdev *device = container_of(kp, struct cdev, kobj);

Kobject basics (2/3) Initialization Set the entire kobject to 0, memset() Set up some

Kobject basics (2/3) Initialization Set the entire kobject to 0, memset() Set up some of fields with kobject_init(), ex. reference count to 1 Set the name by kobject_set_name(kobj, char *format, …) Set up the other field, such as ktype, kset and parent Reference count struct kobject *kobject_get(struct kobject *kobj); //++ void kobject_put(struct kobject *kobj); //--, 0 to cleanup “struct module *owner” in struct cdev? The existence of a kobject require the existence of module that created that kobject. ex. cdev_get()

Kobject basics (3/3) Release functions Even predictable object life cycles become more complicated when

Kobject basics (3/3) Release functions Even predictable object life cycles become more complicated when sysfs is brought in; user-space programs can keep a reference for an arbitrary period of time. 也許因為 擴充或 Every kobject must have a release method. overload方便 The release method is not stored in the kobject itself kobject types – kobj_type struct kobj_type { void (*release)(struct kobject *); 跟sysfs有關 struct sysfs_ops * sysfs_ops; struct attribute ** default_attrs; }; The kobject contains a field, pointer ktype If kobject is a member of kset, the pointer provided by kset struct kobj_type *get_ktype(struct kobject*kobj);

Kobject hierarchies, kset The parent pointer and ksets “parent” points to another kobject, representing

Kobject hierarchies, kset The parent pointer and ksets “parent” points to another kobject, representing the next level up “kset” is a collection of kobjects kset are always represented in sysfs Every kobject that is a member of a kset is represented in sysfs

ksets Adding a kobject to a kset kobject’s kset must be pointed at the

ksets Adding a kobject to a kset kobject’s kset must be pointed at the kset of interest Call kobject_add(struct kobject *kobj); // reference count ++ kobject_init( ) + kobject_add( ) kobject_register( ) Removing from the kset kobject_del( ) + kobject_put( ) kobject_unregister( ) Operation on ksets void kset_init(struct kset *kset); int kset_add(struct kset *kset); int kset_register(struct kset *kset); void kset_unregister(struct kset *kset); struct kset *kset_get(struct kset *kset); void kset_put(struct kset *kset); ktype, is used in preference to the ktype in a kobject

Kobjects’ hierarchy of block subsystem

Kobjects’ hierarchy of block subsystem

Subsystems Representation for a high-level portion of the kernel Usually show up at the

Subsystems Representation for a high-level portion of the kernel Usually show up at the top of the sysfs Block devices, block_subsys, /sys/block Core device hierarchy, devices_subsys, /sys/devices Every bus type known to the kernel… Driver authors almost never needs to create one Probably want is to add a new “class” Subsystem is really just a wrapper around a kset struct subsystem { struct kset; struct rw_semaphore rwsem; // used to serialize access };

Subsystems fs/char_dev. c, line 442 subsystem_init(&cdev_subsys); //not public in sysfs drivers/firmware/efivars. c, line 689

Subsystems fs/char_dev. c, line 442 subsystem_init(&cdev_subsys); //not public in sysfs drivers/firmware/efivars. c, line 689 subsystem_register(&vars_subsys); // Extensible Firmware Interface (EFI) drivers/pci/hotplug/pci_hotplug_core. c, line 672 subsystem_register(&pci_hotplug_slots_subsys); drivers/base/sys. c, line 392 subsystem_register(&system_subsys); //pseudo-bus for cpus, PICs, timers, etc… drivers/base/core. c, line 423 subsystem_register(&devices_subsys); drivers/base/bus. c: line 697 subsystem_register(&bus->subsys); drivers/base/bus. c: line 745 subsystem_register(&bus_subsys); drivers/block/genhd. c, line 307 subsystem_register(&block_subsys); drivers/base/class. c: line 148 subsystem_register(&cls->subsys); drivers/base/class. c: line 567 subsystem_register(&class_subsys); fs/debugfs/inode. c, line 308 subsystem_register(&debug_subsys); kernel/power/main. c, line 259 subsystem_register(&power_subsys); kernel/params. c, line 690 subsystem_register(&module_subsys); kernel/ksysfs. c, line 49 subsystem_register(&kernel_subsys); //kernel sysfs attr security/seclvl. c, line 655 subsystem_register(&seclvl_subsys) // BSD Secure Levels LSM drivers/base/firmware. c: line 20 subsystem_register(s); drivers/base/firmware. c: line 30 subsystem_register(&firmware_subsys);

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together

Low-level sysfs operations Every kobject exports attributes, in that its sysfs dir #include <linux/sysfs.

Low-level sysfs operations Every kobject exports attributes, in that its sysfs dir #include <linux/sysfs. h> { Call kobject_add( ) to show up in sysfs kfree(); } Default attributes struct attribute { char *name; struct module *owner; mode_t mode; }; kobj_type (*release)( ) *sysfs_ops **default_attrs sysfs_ops *(show) *(store) { snprintf(); } attribute “version” * struct sysfs_ops { ssize_t (*show)(*kobj, struct attribute *attr, char *buffer); S_IRUGO ssize_t (*store)(*kobj, struct attribute *attr, const char *buffer, size_t size); PAGE_SIZE };

Low-level sysfs operations Non default attributes Attributes can be added and removed at will

Low-level sysfs operations Non default attributes Attributes can be added and removed at will int sysfs_create_file(struct kobject *kobj, struct attribute *attr); int sysfs_remove_file(struct kobject *kobj, struct attribute *attr); The same show() and store() are called Binary attributes e. g. , when a device is hot-plugged, a user-space program can be started via hot-plug mechanism and then passes the firmware code struct bin_attribute { struct attribute attr; size_t size; ssize_t (*read)(struct kobject *kobj, char *buffer, loff_t pos, size_t size); ssize_t (*write)(struct kobject *kobj, char *buffer, loff_t pos, size_t size); }; int sysfs_create_bin_file(*kobj, struct bin_attribute *attr); int sysfs_remove_bin_file(*kobj, struct bin_attribute *attr); Symbolic links int sysfs_create_link(*kobj, struct kobject *target, char *name); void sysfs_remove_link(*kobj, char *name);

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together

Hotplug event generation Hotplug event a notification to user space from the kernel that

Hotplug event generation Hotplug event a notification to user space from the kernel that something has changed in the system’s configuration is generated whenever a kobject is created (kobject_add) or destroyed (kobject_del) e. g. , a camera is plugged in USB cable, disk is repartitioned… To invoke /sbin/hotplug /proc/sys/kernel/hotplug specifies hotplug program path Operations in “hotplug_ops” of kset Search up via parent until finding a kset (*filter): to suppress hotplug event generation (*name): to pass the name of relevant subsystem for a parameter (*hotplug): to add useful environment variables for hotplug script 詳細運作容後再述

Hotplug operations’ sample code Filter example User space may want to react to the

Hotplug operations’ sample code Filter example User space may want to react to the addition of a disk or a partition, but it does not normally care about request queues. static int block_hotplug_filter(struct kset *kset, struct kobject *kobj) { struct kobj_type *ktype = get_ktype(kobj); return ((ktype = = &ktype_block) 配著前面的 || (ktype = = &ktype_part)); block } subsystem 圖 The generation of hotplug events is usually handled by logic at the bus driver level

kobject_hotplug( ) Called by kobject_register( ) kobject_hotplug( ) 努力的往爸爸方向找kset 呼叫kset/subsystem的filter() 呼叫kset/subsystem的name() 作為/sbin/hotplug 參數$1 呼叫kset/subsystem的hotplug()

kobject_hotplug( ) Called by kobject_register( ) kobject_hotplug( ) 努力的往爸爸方向找kset 呼叫kset/subsystem的filter() 呼叫kset/subsystem的name() 作為/sbin/hotplug 參數$1 呼叫kset/subsystem的hotplug() 建構環境變數 call_usermodehelper( ) Setup a completion without wait 0 to skip return

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together

Buses, devices, and drivers Buses Channel between the processor and one or more devices

Buses, devices, and drivers Buses Channel between the processor and one or more devices Devices and device drivers Once again, much of the material covered here will never be needed by many driver authors. device struct ldd_device Driver kobject core bus driver struct ldd_driver struct device driver Functional view inside kernel struct kobject

Buses (0/2) struct bus_type { char *name; struct subsystem subsys; struct kset drivers; struct

Buses (0/2) struct bus_type { char *name; struct subsystem subsys; struct kset drivers; struct kset devices; int (*match)(struct device *dev, struct device_driver *drv); struct device *(*add)(struct device * parent, char * bus_id); int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); /* Some fields omitted */ };

Buses (1/2) For example, lddbus in example. tgz Bus registration struct bus_type ldd_bus_type =

Buses (1/2) For example, lddbus in example. tgz Bus registration struct bus_type ldd_bus_type = {. name = "ldd", . match = ldd_match, //容後再述. hotplug = ldd_hotplug, //容後再述 }; int __init ldd_bus_init(void) { ret = bus_register(&ldd_bus_type); //ret value must be checked … // 在bus subsystem下, /sys/bus/ldd ret = device_register(&ldd_bus); Deregistration void ldd_bus_exit(void){ device_unregister(&ldd_bus); bus_unregister(&ldd_bus_type);

Buses (2/2) Bus methods int (*match)(struct device *device, struct device_driver *driver); Called whenever a

Buses (2/2) Bus methods int (*match)(struct device *device, struct device_driver *driver); Called whenever a new device or driver is added for this bus Return a nonzero value if the device can be handled by driver static int ldd_match(struct device *dev, struct device_driver *driver) { return !strncmp(dev->bus_id, driver->name, strlen(driver->name)); } int (*hotplug) (struct device *device, char **envp, int num_envp, char *buffer, int buffer_size); Allow the bus to add environment variables 直接看範例程式, LDDBUS_VERSION Iterating over devices and drivers bus_for_each_dev( ), bus_for_each_drv( ) Bus attributes struct bus_attribute, (*show), (*store) BUS_ATTR(name, mode, show, store); declare “struct bus_attr_name” bus_create_file( ), bus_remove_file( ) 看lddbus的BUS_ATTR(version

Devices (0/1) struct device { Must be set before registering struct device *parent; struct

Devices (0/1) struct device { Must be set before registering struct device *parent; struct kobject kobj; device->kobj->parent char bus_id[BUS_ID_SIZE]; == &device->parent->kobj struct bus_type *bus; struct device_driver *driver; kobject_unregister( ) void *driver_data; void (*release)(struct device *dev); /* Several fields omitted */ kobject_hotplug() kobject_del() kobject_put() }; kobject_release( ) kset’s release 也就是device_release( ) dev->release( )

Devices (1/1) Device registration int device_register(struct device *dev); void device_unregister(struct device *dev); An actual

Devices (1/1) Device registration int device_register(struct device *dev); void device_unregister(struct device *dev); An actual bus is a device and must be registered static void ldd_bus_release(struct device *dev) { printk(KERN_DEBUG "lddbus releasen"); } struct device ldd_bus = {. bus_id = "ldd 0", . release = ldd_bus_release }; // device_register( ) & unregister( ) 在 ldd_bus_init( ) & exit( )被叫 // 在devices subsystem下, /sys/devices/ldd 0/ Device attributes struct device_attribute, DEVICE_ATTR( ), device_create_file, …

Device structure embedding for a specific bus (e. g. , pci_dev, ldd_device) “struct device”

Device structure embedding for a specific bus (e. g. , pci_dev, ldd_device) “struct device” contains the device core’s information Most subsystems track other about the devices they host As a result, “struct device” is usually embedded lddbus creates its own device type for ldd devices struct ldd_device { char *name; struct ldd_driver *driver; struct device dev; }; #define to_ldd_device(dev) container_of(dev, struct ldd_device, dev); sculld 多了device register之類動作, 納入sysfs, 理論上可以 hotplug; scullp僅有module下有

Device drivers struct device_driver { char *name; struct bus_type *bus; struct kobject kobj; struct

Device drivers struct device_driver { char *name; struct bus_type *bus; struct kobject kobj; struct list_head devices; int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown) (struct device *dev); };

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together

Classes net/core/net-sysfs. c, line 460 class_register(&net_class); net/bluetooth/hci_sysfs. c, line 147 class_register(&bt_class); drivers/pcmcia/cs. c, line

Classes net/core/net-sysfs. c, line 460 class_register(&net_class); net/bluetooth/hci_sysfs. c, line 147 class_register(&bt_class); drivers/pcmcia/cs. c, line 1892 class_register(&pcmcia_socket_class); drivers/usb/core/file. c: line 90 class_register(&usb_class); drivers/usb/core/hcd. c, line 649 class_register(&usb_host_class); drivers/pci/probe. c, line 110 class_register(&pcibus_class); 還有很多

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction

Outlines Base type Kobjects, Ksets, and Subsystems Low-level sysfs operations Derived type and interaction Hotplug event generation Buses, devices, and drivers High level view Classes Put it all together 番外篇 – Dealing with Firmware