Linux Device Drivers 2nd Edition (779877), страница 25
Текст из файла (страница 25)
Oneis that the flush method was added during the 2.1 development cycle. Driver writers almost never need to worry about this method, but its presence in the middleof the structure can still create problems. The best way to avoid dealing with theflush method is to use the tagged initialization syntax, as we did in all the samplesource files.The other difference is in the way an inode pointer is retrieved from a filppointer. Whereas modern kernels use a dentry (directory entry) data structure,version 2.0 had no such structure.
Therefore, sysdep.h defines a macro that shouldbe used to portably access an inode from a filp:#ifdef LINUX_20# define INODE_FROM_F(filp) ((filp)->f_inode)#else# define INODE_FROM_F(filp) ((filp)->f_dentry->d_inode)#endifThe Module Usage CountIn 2.2 and earlier kernels, the Linux kernel did not offer any assistance to modulesin maintaining the usage count. Modules had to do that work themselves. Thisapproach was error prone and required the duplication of a lot of work. It alsoencouraged race conditions. The new method is thus a definite improvement.Code that is written to be portable, however, must be prepared to deal with theolder way of doing things.
That means that the usage count must still be incremented when a new reference is made to the module, and decremented whenthat reference goes away. Portable code must also work around the fact that theowner field did not exist in the file_operations structure in earlier kernels.9322 June 2001 16:35http://openlib.org.uaChapter 3: Char DriversThe easiest way to handle that is to use SET_MODULE_OWNER, rather than working with the owner field directly. In sysdep.h, we provide a nullSET_FILE_OWNER for kernels that do not have this facility.Changes in Semaphore SupportSemaphore support was less developed in the 2.0 kernel; support for SMP systemsin general was primitive at that time. Drivers written for only that kernel versionmay not need to use semaphores at all, since only one CPU was allowed to berunning kernel code at that time.
Nonetheless, there may still be a need forsemaphores, and it does not hurt to have the full protection needed by later kernelversions.Most of the semaphore functions covered in this chapter existed in the 2.0 kernel.The one exception is sema_init; in version 2.0, programmers had to initializesemaphores manually.
The sysdep.h header file handles this problem by defining aversion of sema_init when compiled under the 2.0 kernel:#ifdef LINUX_20# ifdef MUTEX_LOCKED /* Only if semaphore.h included */extern inline void sema_init (struct semaphore *sem, int val){sem->count = val;sem->waking = sem->lock = 0;sem->wait = NULL;}# endif#endif /* LINUX_20 */Changes in Access to User SpaceFinally, access to user space changed completely at the beginning of the 2.1 development series.
The new interface has a better design and makes much better useof the hardware in ensuring safe access to user-space memory. But, of course, theinterface is different. The 2.0 memory-access functions were as follows:void memcpy_fromfs(void *to, const void *from, unsigned long count);void memcpy_tofs(void *to, const void *from, unsigned long count);The names of these functions come from the historical use of the FS segment register on the i386.
Note that there is no return value from these functions; if theuser supplies an invalid address, the data copy will silently fail. sysdep.h hides therenaming and allows you to portably call copy_to_user and copy_fr om_user.9422 June 2001 16:35http://openlib.org.uaQuick ReferenceQuick ReferenceThis chapter introduced the following symbols and header files. The list of thefields in struct file_operations and struct file is not repeated here.#include <linux/fs.h>The “file system” header is the header required for writing device drivers.
Allthe important functions are declared in here.int register_chrdev(unsigned int major, const char*name, struct file_operations *fops);Registers a character device driver. If the major number is not 0, it is usedunchanged; if the number is 0, then a dynamic number is assigned for thisdevice.int unregister_chrdev(unsigned int major, const char *name);Unregisters the driver at unload time. Both major and the name string mustcontain the same values that were used to register the driver.kdev_t inode->i_rdev;The device “number” for the current device is accessible from the inodestructure.int MAJOR(kdev_t dev);int MINOR(kdev_t dev);These macros extract the major and minor numbers from a device item.kdev_t MKDEV(int major, int minor);This macro builds a kdev_t data item from the major and minor numbers.SET_MODULE_OWNER(struct file_operations *fops)This macro sets the owner field in the given file_operations structure.#include <asm/semaphore.h>Defines functions and types for the use of semaphores.void sema_init (struct semaphore *sem, int val);Initializes a semaphore to a known value.
Mutual exclusion semaphores areusually initialized to a value of 1.int down_interruptible (struct semaphore *sem);void up (struct semaphore *sem);Obtains a semaphore (sleeping, if necessary) and releases it, respectively.#include <asm/segment.h>#include <asm/uaccess.h>segment.h defines functions related to cross-space copying in all kernels up toand including 2.0. The name was changed to uaccess.h in the 2.1development series.9522 June 2001 16:35http://openlib.org.uaChapter 3: Char Driversunsigned long _ _copy_from_user (void *to, const void *from,unsigned long count);unsigned long _ _copy_to_user (void *to, const void *from,unsigned long count);Copy data between user space and kernel space.void memcpy_fromfs(void *to, const void *from, unsigned longcount);void memcpy_tofs(void *to, const void *from, unsigned longcount);These functions were used to copy an array of bytes from user space to kernelspace and vice versa in version 2.0 of the kernel.#include <linux/devfs_fs_kernel.h>devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char*name, void *info);devfs_handle_t devfs_register (devfs_handle_t dir, constchar *name, unsigned int flags,unsigned int major, unsigned int minor, umode_t mode, void*ops, void *info);void devfs_unregister (devfs_handle_t de);These are the basic functions for registering devices with the device filesystem(devfs).9622 June 2001 16:35http://openlib.org.uaCHAPTER FOURDEBUGGINGTECHNIQUESOne of the most compelling problems for anyone writing kernel code is how toapproach debugging.
Kernel code cannot be easily executed under a debugger,nor can it be easily traced, because it is a set of functionalities not related to a specific process. Kernel code errors can also be exceedingly hard to reproduce andcan bring down the entire system with them, thus destroying much of the evidence that could be used to track them down.This chapter introduces techniques you can use to monitor kernel code and traceerrors under such trying circumstances.Debugging by PrintingThe most common debugging technique is monitoring, which in applications programming is done by calling printf at suitable points. When you are debuggingkernel code, you can accomplish the same goal with printk.printkWe used the printk function in earlier chapters with the simplifying assumptionthat it works like printf.
Now it’s time to introduce some of the differences.One of the differences is that printk lets you classify messages according to theirseverity by associating different loglevels, or priorities, with the messages. You usually indicate the loglevel with a macro. For example, KERN_INFO, which we sawprepended to some of the earlier print statements, is one of the possible loglevelsof the message.
The loglevel macro expands to a string, which is concatenated tothe message text at compile time; that’s why there is no comma between the priority and the format string in the following examples. Here are two examples ofprintk commands, a debug message and a critical message:9722 June 2001 16:35http://openlib.org.uaChapter 4: Debugging Techniquesprintk(KERN_DEBUG "Here I am: %s:%i\n", _ _FILE_ _, _ _LINE_& _);printk(KERN_CRIT "I’m trashed; giving up on %p\n", ptr);There are eight possible loglevel strings, defined in the header <linux/kernel.h>:KERN_EMERGUsed for emergency messages, usually those that precede a crash.KERN_ALERTA situation requiring immediate action.KERN_CRITCritical conditions, often related to serious hardware or software failures.KERN_ERRUsed to report error conditions; device drivers will often use KERN_ERR toreport hardware difficulties.KERN_WARNINGWarnings about problematic situations that do not, in themselves, create serious problems with the system.KERN_NOTICESituations that are normal, but still worthy of note.
A number of securityrelated conditions are reported at this level.KERN_INFOInformational messages. Many drivers print information about the hardwarethey find at startup time at this level.KERN_DEBUGUsed for debugging messages.Each string (in the macro expansion) represents an integer in angle brackets. Integers range from 0 to 7, with smaller values representing higher priorities.A printk statement with no specified priority defaults to DEFAULT_MESSAGE_LOGLEVEL, specified in ker nel/printk.c as an integer. The default loglevelvalue has changed several times during Linux development, so we suggest thatyou always specify an explicit loglevel.Based on the loglevel, the kernel may print the message to the current console, beit a text-mode terminal, a serial line printer, or a parallel printer.