Quick Reference for Linux Device Drivers Development (kernel 2.6.15)
Posted on 2012-01-21 00:58 shwh 阅读(385) 评论(0) 编辑 收藏 举报
Concurrency and race conditions
#include <asm/atomic.h>
#include <asm/bitops.h>
#include <linux/spinlock.h>
#include <linux/seqlock.h>
#include <linux/rcupdate.h>
#include <linux/semaphore.h>
#include <linux/rwsem.h>
#include <linux/completion.h>
atomic operation
#include <asm/atomic.h>
atomic_t v = ATOMIC_INIT(value);
void atomic_set(atomic_t *v, int i);
int atomic_read(atomic_t *v);
void atomic_add(int i, atomic_t *v);
void atomic_sub(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
int atomic_add_negative(int i, atomic_t *v);
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
Atomically access integer variables. The atomic_t variables must be accessed
only through these functions.
#include <asm/bitops.h>
void set_bit(nr, void *addr);
void clear_bit(nr, void *addr);
void change_bit(nr, void *addr);
test_bit(nr, void *addr);
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
Atomically access bit values; they can be used for flags or lockvariables. Using
these functions prevents any race condition related to concurrent access to the
bit.
spinlock
#include <linux/spinlock.h>
spinlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock_init(spinlock_t *lock);
The include file defining the spinlockinterface and the two ways of initializing
locks.
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock);
void spin_lock_bh(spinlock_t *lock);
The various ways of locking a spinlock and, possibly, disabling interrupts.
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
Nonspinning versions of the above functions; these return 0 in case of failure to
obtain the lock, nonzero otherwise.
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
The corresponding ways of releasing a spinlock.
reader-writer spin-lock
rwlock_t lock = RW_LOCK_UNLOCKED
rwlock_init(rwlock_t *lock);
The two ways of initializing reader/writer locks.
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
Functions for obtaining read access to a reader/writer lock.
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
Functions for releasing read access to a reader/writer spinlock.
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
Functions for obtaining write access to a reader/writer lock.
void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
Functions for releasing write access to a reader/writer spinlock.
sequential spin-lock
#include <linux/seqlock.h>
seqlock_t lock = SEQLOCK_UNLOCKED;
seqlock_init(seqlock_t *lock);
The include file defining seqlocks and the two ways of initializing them.
unsigned int read_seqbegin(seqlock_t *lock);
unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
int read_seqretry(seqlock_t *lock, unsigned int seq);
int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long
flags);
Functions for obtaining read access to a seqlock-protected resources.
Notice: <Linux Device Driver 3rd>says, every time after reading the shared resource pointed by the parameter
"lock" of the function "read_seqbegin(seqlock_t* lock)" or
"read_seqbegin_irqsave(seqlock_t* lock, unsigned long flags)",
the corresponding one of last two functions must be called to check if the read data has changed during that time.(???Really?It's opposite to the saying
from <Linux Kernel Development 3rd>.)
void write_seqlock(seqlock_t *lock);
void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);
void write_seqlock_irq(seqlock_t *lock);
void write_seqlock_bh(seqlock_t *lock);
int write_tryseqlock(seqlock_t *lock);
Functions for obtaining write access to a seqlock-protected resource.
void write_sequnlock(seqlock_t *lock);
void write_sequnlock_irqrestore(seqlock_t *lock, unsigned long flags);
void write_sequnlock_irq(seqlock_t *lock);
void write_sequnlock_bh(seqlock_t *lock);
Functions for releasing write access to a seqlock-protected resource.
read-copy-update spin-lock
#include <linux/rcupdate.h>
The include file required to use the read-copy-update (RCU) mechanism.
void rcu_read_lock;
void rcu_read_unlock;
Macros for obtaining atomic read access to a resource protected by RCU.
void call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg);
Arranges for a callbackto run after all processors have been scheduled and an
RCU-protected resource can be safely freed.
semaphore
#include <linux/semaphore.h>
The include file that defines semaphores and the operations on them.
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
Two macros for declaring and initializing a semaphore used in mutual exclusion mode.
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
These two functions can be used to initialize a semaphore at runtime.
void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_trylock(struct semaphore *sem);
void up(struct semaphore *sem);
Lockand unlocka semaphore. down puts the calling process into an uninterruptible
sleep if need be; down_interruptible, instead, can be interrupted by a signal.
down_trylock does not sleep; instead, it returns immediately if the
semaphore is unavailable. Code that locks a semaphore must eventually unlock
it with up.
Reader-writer semaphore
#include <linux/rwsem.h>
struct rw_semaphore;
init_rwsem(struct rw_semaphore *sem);
The reader/writer version of semaphores and the function that initializes it.
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
Functions for obtaining and releasing read access to a reader/writer semaphore.
void down_write(struct rw_semaphore *sem)
int down_write_trylock(struct rw_semaphore *sem)
void up_write(struct rw_semaphore *sem)
void downgrade_write(struct rw_semaphore *sem)
Functions for managing write access to a reader/writer semaphore.
competion
#include <linux/completion.h>
DECLARE_COMPLETION(name);
init_completion(struct completion *c);
INIT_COMPLETION(struct completion c);
The include file describing the Linux completion mechanism, and the normal
methods for initializing completions. INIT_COMPLETION should be used only to
reinitialize a completion that has been previously used.
void wait_for_completion(struct completion *c);
Wait for a completion event to be signalled.
void complete(struct completion *c);
void complete_all(struct completion *c);
Signal a completion event. complete wakes, at most, one waiting thread, while
complete_all wakes all waiters.
void complete_and_exit(struct completion *c, long retval);
Signals a completion event by calling complete and calls exit for the current
thread.
Block Drivers
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/fs.h>
int register_blkdev(unsigned int major, const char *name);
int unregister_blkdev(unsigned int major, const char *name);
register_blkdev registers a block driver with the kernel and, optionally, obtains a
major number(When "major" is zero). A driver can be unregistered with unregister_blkdev.
Notice:
"major 参数是块设备要使用的主设备号,name 为设备名,它会在/proc/devices 中
被显示。如果major 为0,内核会自动分配一个新的主设备号,register_blkdev()函数
的返回值就是这个主设备号。如果register_blkdev()返回一个负值,表明发生了一个错
误。在Linux 2.6 内核中,对register_blkdev()的调用完全是可选的,
register_blkdev()的功能已随时间正在减少,这个调用最多只完成两件事。
① 如果需要,分配一个动态主设备号。
② 在/proc/devices 中创建一个入口。
在将来的内核中,register_blkdev()可能会被去掉。但是目前的大部分驱动仍然调用它。"
struct block_device_operations
Structure that holds most of the methods for block drivers.Just like "struct file_operation".
#include <linux/genhd.h>
struct gendisk;
Structure that describes a single block device within the kernel.
"在 Linux 内核中,使用gendisk(通用磁盘)结构体来表示1 个独立的磁盘设备(或分区)"
struct gendisk *alloc_disk(int minors);
Function that allocate gendisk structures and return them to the system.
void set_capacity(struct gendisk *gd, sector_t sectors);
Stores the capacity of the device (in 512-byte sectors) within the gendisk structure.
Notice:
"不管物理设备的真实扇区大小是多少,内核与块设备驱动交互的扇区都以512 字
节为单位。因此,set_capacity()函数也以512字节为单位。"
void add_disk(struct gendisk *gd);
Adds a disk to the kernel. As soon as this function is called, your disk’s methods
can be invoked by the kernel.
Notice:
"add_disk()的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。"
void del_gendisk(struct gendisk *gd);
Delete a disk from the kernel.
int check_disk_change(struct block_device *bdev);
A kernel function that checks for a media change in the given disk drive and
takes the required cleanup action when such a change is detected.
#include <linux/blkdev.h>
request_queue_t blk_init_queue(request_fn_proc *request, spinlock_t *lock);
A function that handles the creation of block request queues.
The arguments are, of course, the request function for this queue and a spinlock that
controls access to the queue. This function allocates memory (quite a bit of memory,
actually) and can fail because of this; you should always check the return value
before attempting to use the queue.
As part of the initialization of a request queue, you can set the field queuedata (which
is a void * pointer) to any value you like. This field is the request queue’s equivalent
to the private_data we have seen in other structures.
void blk_cleanup_queue(request_queue_t *);
A function that handles the deletion of block request queues. To return a request queue to
the system (at module unload time, generally).
struct request *elv_next_request(request_queue_t *queue);
void end_request(struct request *req, int success);
elv_next_request obtains the next request from a request queue; end_request may
be used in very simple drivers to mark the completion of (or part of) a request.
Notice:
elv_next_request returns a pointer to the next request to process (as determined by the I/O scheduler)
or NULL if no more requests remain to be processed. elv_next_request leaves the request
on the queue but marks it as being active; this mark prevents the I/O scheduler from
attempting to merge other requests with this one once you start to execute it.
void blkdev_dequeue_request(struct request *req);
void elv_requeue_request(request_queue_t *queue, struct request *req);
Functions that remove a request from a queue and put it back on if necessary.
Notice:
If your driver operates on multiple requests from the same queue simultaneously, it
must dequeue them in this manner.
void blk_stop_queue(request_queue_t *queue);
void blk_start_queue(request_queue_t *queue);
If you need to prevent further calls to your request method, a call to blk_stop_queue
does the trick. Acall to blk_start_queue is necessary to cause your request method
to be invoked again. The queue lock must be held when calling either of these functions.
void blk_queue_bounce_limit(request_queue_t *queue, u64 dma_addr);
void blk_queue_max_sectors(request_queue_t *queue, unsigned short max);
void blk_queue_max_phys_segments(request_queue_t *queue, unsigned short max);
void blk_queue_max_hw_segments(request_queue_t *queue, unsigned short max);
void blk_queue_max_segment_size(request_queue_t *queue, unsigned int max);
blk_queue_segment_boundary(request_queue_t *queue, unsigned long mask);
void blk_queue_dma_alignment(request_queue_t *queue, int mask);
void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max);
Functions that set various queue parameters that control how requests are created
for a particular device.
#include <linux/bio.h>
struct bio;
Low-level structure representing a portion of a block I/O request.
bio_sectors(struct bio *bio);
bio_data_dir(struct bio *bio);
Two macros that yield the size and direction of a transfer described by a bio
structure.
bio_for_each_segment(bvec, bio, segno);
A pseud ocontrol structure used to loop through the segments that make up a bio
structure.
char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);
void __bio_kunmap_atomic(char *buffer, enum km_type type);
__bio_kmap_atomic may be used to create a kernel virtual address for a given
segment within a bio structure. The mapping must be undone with __bio_
kunmap_atomic.
struct page *bio_page(struct bio *bio);
int bio_offset(struct bio *bio);
int bio_cur_sectors(struct bio *bio);
char *bio_data(struct bio *bio);
char *bio_kmap_irq(struct bio *bio, unsigned long *flags);
void bio_kunmap_irq(char *buffer, unsigned long *flags);
A set of accessor macros that provide access to the “current” segment within a
bio structure.
void blk_queue_ordered(request_queue_t *queue, int flag);
int blk_barrier_rq(struct request *req);
Call blk_queue_ordered if your driver implements barrier requests—as it should.
The macro blk_barrier_rq returns a nonzero value if the current request is a barrier
request.
int blk_noretry_request(struct request *req);
This macro returns a nonzero value if the given request should not be retried on
errors.
int end_that_request_first(struct request *req, int success, int count);
void end_that_request_last(struct request *req);
Use end_that_request_first to indicate completion of a portion of a block I/O
request. When that function returns 0, the request is complete and should be
passed to end_that_request_last.
rq_for_each_bio(bio, request)
Another macro-implemented control structure; it steps through each bio that
makes up a request.
int blk_rq_map_sg(request_queue_t *queue, struct request *req, struct scatterlist *list);
Fills the given scatterlist with the information needed to map the buffers in the
given request for a DMA transfer.
typedef int (make_request_fn) (request_queue_t *q, struct bio *bio);
The prototype for the make_request function.
void bio_endio(struct bio *bio, unsigned int bytes, int error);
Signal completion for a given bio. This function should be used only if your
driver obtained the bio directly from the block layer via the make_request function.
request_queue_t *blk_alloc_queue(int flags);
void blk_queue_make_request(request_queue_t *queue, make_request_fn *func);
Use blk_alloc_queue to allocate a request queue that is used with a custom
make_request function. That function should be set with blk_queue_make_
request.
typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req);
void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);
The prototype and setup functions for a command preparation function, which
can be used to prepare the necessary hardware command before the request is
passed to your request function.
int blk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);
int blk_queue_resize_tags(request_queue_t *queue, int new_depth);
int blk_queue_start_tag(request_queue_t *queue, struct request *req);
void blk_queue_end_tag(request_queue_t *queue, struct request *req);
struct request *blk_queue_find_tag(request_queue_t *qeue, int tag);
void blk_queue_invalidate_tags(request_queue_t *queue);
Support functions for drivers using tagged command queueing.