Linux BIO

bio.h

static inline struct bio *bio_kmalloc(gfp_t gfp_mask, unsigned int nr_iovecs)
{
return bio_alloc_bioset(gfp_mask, nr_iovecs, NULL);
}

blk_rq_map_user_iov - map user data to a request, for REQ_TYPE_BLOCK_PC usage

scsi_ioctl.c

static int sg_io(struct request_queue *q, struct gendisk *bd_disk,

struct sg_io_hdr *hdr, fmode_t mode);

scst/sg.c

static int sg_start_req(Sg_request *srp, unsigned char *cmd);

=> scsi_dispatch_cmd

=> scsi_request_fn

=> __blk_run_queue_uncond

=> __blk_run_queue

=> blk_queue_bio

=> generic_make_request

=> submit_bio

=> submit_bh

* generic_make_request - hand a buffer to its device driver for I/O

* @bio:  The bio describing the location in memory and on the device.

*

* generic_make_request() is used to make I/O requests of block

* devices. It is passed a &struct bio, which describes the I/O that needs

* to be done.

submit_bio - submit a bio to the block device layer for I/O

void submit_bio(int rw, struct bio *bio);

void generic_make_request(struct bio *bio);

在每个进程的task_struct中,都包含有两个变量----struct bio *bio_list,实际的提交操作会由generic_make_request()调用__generic_make_request()函数完成。

/* stacked block device info */

struct bio_list *bio_list;

/**
 * generic_make_request - hand a buffer to its device driver for I/O
 * @bio:  The bio describing the location in memory and on the device.
 *
 * generic_make_request() is used to make I/O requests of block
 * devices. It is passed a &struct bio, which describes the I/O that needs
 * to be done.
 *
 * generic_make_request() does not return any status.  The
 * success/failure status of the request, along with notification of
 * completion, is delivered asynchronously through the bio->bi_end_io
 * function described (one day) else where.
 *
 * The caller of generic_make_request must make sure that bi_io_vec
 * are set to describe the memory buffer, and that bi_dev and bi_sector are
 * set to describe the device address, and the
 * bi_end_io and optionally bi_private are set to describe how
 * completion notification should be signaled.
 *
 * generic_make_request and the drivers it calls may use bi_next if this
 * bio happens to be merged with someone else, and may resubmit the bio to
 * a lower device by calling into generic_make_request recursively, which
 * means the bio should NOT be touched after the call to ->make_request_fn.
 */
void generic_make_request(struct bio *bio)
{
	struct bio_list bio_list_on_stack;

	if (!generic_make_request_checks(bio))
		return;

	/*
	 * We only want one ->make_request_fn to be active at a time, else
	 * stack usage with stacked devices could be a problem.  So use
	 * current->bio_list to keep a list of requests submited by a
	 * make_request_fn function.  current->bio_list is also used as a
	 * flag to say if generic_make_request is currently active in this
	 * task or not.  If it is NULL, then no make_request is active.  If
	 * it is non-NULL, then a make_request is active, and new requests
	 * should be added at the tail
	 */
	if (current->bio_list) {
		bio_list_add(current->bio_list, bio);
		return;
	}

	/* following loop may be a bit non-obvious, and so deserves some
	 * explanation.
	 * Before entering the loop, bio->bi_next is NULL (as all callers
	 * ensure that) so we have a list with a single bio.
	 * We pretend that we have just taken it off a longer list, so
	 * we assign bio_list to a pointer to the bio_list_on_stack,
	 * thus initialising the bio_list of new bios to be
	 * added.  ->make_request() may indeed add some more bios
	 * through a recursive call to generic_make_request.  If it
	 * did, we find a non-NULL value in bio_list and re-enter the loop
	 * from the top.  In this case we really did just take the bio
	 * of the top of the list (no pretending) and so remove it from
	 * bio_list, and call into ->make_request() again.
	 */
	BUG_ON(bio->bi_next);
	bio_list_init(&bio_list_on_stack);
	current->bio_list = &bio_list_on_stack;
	do {
		struct request_queue *q = bdev_get_queue(bio->bi_bdev);

		将bio发送给disk,调用blk_queue_bio

		q->make_request_fn(q, bio); 

		bio = bio_list_pop(current->bio_list);
	} while (bio);
	current->bio_list = NULL; /* deactivate */
}
void blk_queue_bio(struct request_queue *q, struct bio *bio)
{
	const bool sync = !!(bio->bi_rw & REQ_SYNC);
	struct blk_plug *plug;
	int el_ret, rw_flags, where = ELEVATOR_INSERT_SORT;
	struct request *req;
	unsigned int request_count = 0;

	/*
	 * low level driver can indicate that it wants pages above a
	 * certain limit bounced to low memory (ie for highmem, or even
	 * ISA dma in theory)
	 */
	blk_queue_bounce(q, &bio);

	if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) {
		bio_endio(bio, -EIO);
		return;
	}

	if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
		spin_lock_irq(q->queue_lock);
		where = ELEVATOR_INSERT_FLUSH;
		goto get_rq;
	}

	/*
	 * Check if we can merge with the plugged list before grabbing
	 * any locks.
	 */
	if (blk_attempt_plug_merge(q, bio, &request_count))
		return;

	spin_lock_irq(q->queue_lock);

	el_ret = elv_merge(q, &req, bio);
	if (el_ret == ELEVATOR_BACK_MERGE) {
		if (bio_attempt_back_merge(q, req, bio)) {
			elv_bio_merged(q, req, bio);
			if (!attempt_back_merge(q, req))
				elv_merged_request(q, req, el_ret);
			goto out_unlock;
		}
	} else if (el_ret == ELEVATOR_FRONT_MERGE) {
		if (bio_attempt_front_merge(q, req, bio)) {
			elv_bio_merged(q, req, bio);
			if (!attempt_front_merge(q, req))
				elv_merged_request(q, req, el_ret);
			goto out_unlock;
		}
	}

get_rq:
	/*
	 * This sync check and mask will be re-done in init_request_from_bio(),
	 * but we need to set it earlier to expose the sync flag to the
	 * rq allocator and io schedulers.
	 */
	rw_flags = bio_data_dir(bio);
	if (sync)
		rw_flags |= REQ_SYNC;

	/*
	 * Grab a free request. This is might sleep but can not fail.
	 * Returns with the queue unlocked.
	 */
	req = get_request(q, rw_flags, bio, GFP_NOIO);
	if (unlikely(!req)) {
		bio_endio(bio, -ENODEV);	/* @q is dead */
		goto out_unlock;
	}

	/*
	 * After dropping the lock and possibly sleeping here, our request
	 * may now be mergeable after it had proven unmergeable (above).
	 * We don‘t worry about that case for efficiency. It won‘t happen
	 * often, and the elevators are able to handle it.
	 */
	init_request_from_bio(req, bio);

	if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags))
		req->cpu = raw_smp_processor_id();

	plug = current->plug;
	if (plug) {
		/*
		 * If this is the first request added after a plug, fire
		 * of a plug trace. If others have been added before, check
		 * if we have multiple devices in this plug. If so, make a
		 * note to sort the list before dispatch.
		 */
		if (list_empty(&plug->list))
			trace_block_plug(q);
		else {
			if (request_count >= BLK_MAX_REQUEST_COUNT) {
				blk_flush_plug_list(plug, false);
				trace_block_plug(q);
			}
		}
		list_add_tail(&req->queuelist, &plug->list);
		blk_account_io_start(req, true);
	} else {
		spin_lock_irq(q->queue_lock);
		add_acct_request(q, req, where);
		__blk_run_queue(q);
out_unlock:
		spin_unlock_irq(q->queue_lock);
	}
}
/**
 * __blk_run_queue - run a single device queue
 * @q:	The queue to run
 *
 * Description:
 *    See @blk_run_queue. This variant must be called with the queue lock
 *    held and interrupts disabled.
 */
void __blk_run_queue(struct request_queue *q)
{
	if (unlikely(blk_queue_stopped(q)))
		return;

	__blk_run_queue_uncond(q);
}

/**

* __blk_run_queue_uncond - run a queue whether or not it has been stopped

* @q: The queue to run

*

* Description:

*    Invoke request handling on a queue if there are any pending requests.

*    May be used to restart request handling after a request has completed.

*    This variant runs the queue whether or not the queue has been

*    stopped. Must be called with the queue lock held and interrupts

*    disabled. See also @blk_run_queue.

*/

inline void __blk_run_queue_uncond(struct request_queue *q)

{

if (unlikely(blk_queue_dead(q)))

return;

/*

* Some request_fn implementations, e.g. scsi_request_fn(), unlock

* the queue lock internally. As a result multiple threads may be

* running such a request function concurrently. Keep track of the

* number of active request_fn invocations such that blk_drain_queue()

* can wait until all these request_fn calls have finished.

*/

q->request_fn_active++;

调用请求处理函数

q->request_fn(q);

q->request_fn_active--;

}

时间: 2024-10-28 19:44:34

Linux BIO的相关文章

linux块设备的IO调度算法和回写机制

************************************************************************************** 參考: <Linux内核设计与实现> http://laokaddk.blog.51cto.com/368606/699028/ http://www.cnblogs.com/zhenjing/archive/2012/06/20/linux_writeback.html *************************

《Linux Device Drivers》第十六章 块设备驱动程序——note

简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linux块设备驱动程序接口使得块设备可以发挥其最大的功效,但是其复杂程序又是编程者必须面对的一个问题 一个数据块指的是固定大小的数据,而大小的值由内核确定 数据块的大小通常是4096个字节,但是可以根据体系结构和所使用的文件系统进行改变 与数据块对应的是扇区,它是由底层硬件决定大小的一个块,内核所处理的设备扇区大小是512字节 如果要使用不同的硬件扇区大小,用户必须对内核的扇

linux 块设备驱动(四)——简单的sbull实例

#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #inc

linux块设备驱动---相关结构体(转)

上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成员bd_part指向设备的分区结构.如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk 当用户打开块设备文件时,内核创建结构block_device实例,设备驱动程序还将创建结构gendisk实例,分配请求队列并注册结构block_device实例. 块设备对象结构blo

linux块设备驱动与其测试

1 驱动程序 #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h&g

11 Linux 块设备驱动程序

参考:https://www.cnblogs.com/big-devil/p/8590007.html Linux 块设备驱动程序 概念补充: 块 扇区是硬件数据传输的基本单元,块则是虚拟文件系统传输数据的基本单位.Linux内核中,块的大小必须是2的次幂,但不能超过一个页(4K)的大小. 段 同一物理页面中的硬盘存储介质上连续的多个块组成一个段.段的大小与块的个数有关,段包含多个块,块包含多个扇区.段在内核中由bio_vec结构体描述,多个段的信息存放于结构体bio中的biz_io_vec数组

DMA过程分析

1.1 当我们在应用程序中编写write系统调用,向磁盘中写入数据时,写入请求会先调用底层写函数,将请求先写入内存中的页快速缓存(page cache)中,写入成功则立马返回,真正的写入磁盘操作会延迟运行.Page cache是硬盘在内存中的一个缓存,是linux内核所使用的主要磁盘快速缓存,在绝大多数情况下,内核在读写磁盘时都引用page cache(极少数应用会绕过页快速缓存,如数据库软件). 当把page cache中的一页数据写到块设备之前,内核首先检查相应的页是否已经在快速缓存中,假设

块设备

作者:徐老师,华清远见嵌入式学院讲师. 块设备基本概念 系统中能够随机访问固定大小数据片的设备被称之为块设备.这些数据片就称作块.块设备文件一般都是以安装文 件系统的方式使用,这也是块设备通常的访问方式.块设备的方式访问方式是随机的,也就是可以在访问设备时,随意的从一个位置跳转到另外一个位置.块设备的 访问位置必须能够在介质的不同区间前后移动.所以事实上内核不必提供专门的子系统来管理字符设备,但对于块设备的管理就必须要有一个专门的提供服务的子系 统. 块设备与字符设备的区别 块设备与字符设备的区

[国嵌攻略][147][简单块设备驱动设计]

头文件 #include <linux/blkdev.h> #include <linux/bio.h> blkdev.c /******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #incl