cfq-iosched.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 891 行 · 第 1/2 页

C
891
字号
/* *  linux/drivers/block/cfq-iosched.c * *  CFQ, or complete fairness queueing, disk scheduler. * *  Based on ideas from a previously unfinished io *  scheduler (round robin per-process disk scheduling) and Andrea Arcangeli. * *  Copyright (C) 2003 Jens Axboe <axboe@suse.de> */#include <linux/kernel.h>#include <linux/fs.h>#include <linux/blkdev.h>#include <linux/elevator.h>#include <linux/bio.h>#include <linux/config.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/compiler.h>#include <linux/hash.h>#include <linux/rbtree.h>#include <linux/mempool.h>/* * tunables */static int cfq_quantum = 4;static int cfq_queued = 8;#define CFQ_QHASH_SHIFT		6#define CFQ_QHASH_ENTRIES	(1 << CFQ_QHASH_SHIFT)#define list_entry_qhash(entry)	list_entry((entry), struct cfq_queue, cfq_hash)#define CFQ_MHASH_SHIFT		8#define CFQ_MHASH_BLOCK(sec)	((sec) >> 3)#define CFQ_MHASH_ENTRIES	(1 << CFQ_MHASH_SHIFT)#define CFQ_MHASH_FN(sec)	(hash_long(CFQ_MHASH_BLOCK((sec)),CFQ_MHASH_SHIFT))#define ON_MHASH(crq)		!list_empty(&(crq)->hash)#define rq_hash_key(rq)		((rq)->sector + (rq)->nr_sectors)#define list_entry_hash(ptr)	list_entry((ptr), struct cfq_rq, hash)#define list_entry_cfqq(ptr)	list_entry((ptr), struct cfq_queue, cfq_list)#define RQ_DATA(rq)		((struct cfq_rq *) (rq)->elevator_private)static kmem_cache_t *crq_pool;static kmem_cache_t *cfq_pool;static mempool_t *cfq_mpool;struct cfq_data {	struct list_head rr_list;	struct list_head *dispatch;	struct list_head *cfq_hash;	struct list_head *crq_hash;	unsigned int busy_queues;	unsigned int max_queued;	mempool_t *crq_pool;	request_queue_t *queue;	/*	 * tunables	 */	unsigned int cfq_quantum;	unsigned int cfq_queued;};struct cfq_queue {	struct list_head cfq_hash;	struct list_head cfq_list;	struct rb_root sort_list;	int pid;	int queued[2];#if 0	/*	 * with a simple addition like this, we can do io priorities. almost.	 * does need a split request free list, too.	 */	int io_prio#endif};struct cfq_rq {	struct rb_node rb_node;	sector_t rb_key;	struct request *request;	struct cfq_queue *cfq_queue;	struct list_head hash;};static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq);static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid);static void cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq,			      struct cfq_rq *crq);/* * lots of deadline iosched dupes, can be abstracted later... */static inline void __cfq_del_crq_hash(struct cfq_rq *crq){	list_del_init(&crq->hash);}static inline void cfq_del_crq_hash(struct cfq_rq *crq){	if (ON_MHASH(crq))		__cfq_del_crq_hash(crq);}static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq){	cfq_del_crq_hash(crq);	if (q->last_merge == crq->request)		q->last_merge = NULL;}static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq){	struct request *rq = crq->request;	BUG_ON(ON_MHASH(crq));	list_add(&crq->hash, &cfqd->crq_hash[CFQ_MHASH_FN(rq_hash_key(rq))]);}static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset){	struct list_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)];	struct list_head *entry, *next = hash_list->next;	while ((entry = next) != hash_list) {		struct cfq_rq *crq = list_entry_hash(entry);		struct request *__rq = crq->request;		next = entry->next;		BUG_ON(!ON_MHASH(crq));		if (!rq_mergeable(__rq)) {			__cfq_del_crq_hash(crq);			continue;		}		if (rq_hash_key(__rq) == offset)			return __rq;	}	return NULL;}/* * rb tree support functions */#define RB_NONE		(2)#define RB_EMPTY(node)	((node)->rb_node == NULL)#define RB_CLEAR(node)	((node)->rb_color = RB_NONE)#define RB_CLEAR_ROOT(root)	((root)->rb_node = NULL)#define ON_RB(node)	((node)->rb_color != RB_NONE)#define rb_entry_crq(node)	rb_entry((node), struct cfq_rq, rb_node)#define rq_rb_key(rq)		(rq)->sectorstatic inline void cfq_del_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq){	if (ON_RB(&crq->rb_node)) {		cfqq->queued[rq_data_dir(crq->request)]--;		rb_erase(&crq->rb_node, &cfqq->sort_list);		crq->cfq_queue = NULL;	}}static struct cfq_rq *__cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq){	struct rb_node **p = &cfqq->sort_list.rb_node;	struct rb_node *parent = NULL;	struct cfq_rq *__crq;	while (*p) {		parent = *p;		__crq = rb_entry_crq(parent);		if (crq->rb_key < __crq->rb_key)			p = &(*p)->rb_left;		else if (crq->rb_key > __crq->rb_key)			p = &(*p)->rb_right;		else			return __crq;	}	rb_link_node(&crq->rb_node, parent, p);	return NULL;}static voidcfq_add_crq_rb(struct cfq_data *cfqd, struct cfq_queue *cfqq,struct cfq_rq *crq){	struct request *rq = crq->request;	struct cfq_rq *__alias;	crq->rb_key = rq_rb_key(rq);	cfqq->queued[rq_data_dir(rq)]++;retry:	__alias = __cfq_add_crq_rb(cfqq, crq);	if (!__alias) {		rb_insert_color(&crq->rb_node, &cfqq->sort_list);		crq->cfq_queue = cfqq;		return;	}	cfq_dispatch_sort(cfqd, cfqq, __alias);	goto retry;}static struct request *cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector){	struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->tgid);	struct rb_node *n;	if (!cfqq)		goto out;	n = cfqq->sort_list.rb_node;	while (n) {		struct cfq_rq *crq = rb_entry_crq(n);		if (sector < crq->rb_key)			n = n->rb_left;		else if (sector > crq->rb_key)			n = n->rb_right;		else			return crq->request;	}out:	return NULL;}static void cfq_remove_request(request_queue_t *q, struct request *rq){	struct cfq_data *cfqd = q->elevator.elevator_data;	struct cfq_rq *crq = RQ_DATA(rq);	if (crq) {		struct cfq_queue *cfqq = crq->cfq_queue;		cfq_remove_merge_hints(q, crq);		list_del_init(&rq->queuelist);		if (cfqq) {			cfq_del_crq_rb(cfqq, crq);			if (RB_EMPTY(&cfqq->sort_list))				cfq_put_queue(cfqd, cfqq);		}	}}static intcfq_merge(request_queue_t *q, struct request **req, struct bio *bio){	struct cfq_data *cfqd = q->elevator.elevator_data;	struct request *__rq;	int ret;	ret = elv_try_last_merge(q, bio);	if (ret != ELEVATOR_NO_MERGE) {		__rq = q->last_merge;		goto out_insert;	}	__rq = cfq_find_rq_hash(cfqd, bio->bi_sector);	if (__rq) {		BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector);		if (elv_rq_merge_ok(__rq, bio)) {			ret = ELEVATOR_BACK_MERGE;			goto out;		}	}	__rq = cfq_find_rq_rb(cfqd, bio->bi_sector + bio_sectors(bio));	if (__rq) {		if (elv_rq_merge_ok(__rq, bio)) {			ret = ELEVATOR_FRONT_MERGE;			goto out;		}	}	return ELEVATOR_NO_MERGE;out:	q->last_merge = __rq;out_insert:	*req = __rq;	return ret;}static void cfq_merged_request(request_queue_t *q, struct request *req){	struct cfq_data *cfqd = q->elevator.elevator_data;	struct cfq_rq *crq = RQ_DATA(req);	cfq_del_crq_hash(crq);	cfq_add_crq_hash(cfqd, crq);	if (ON_RB(&crq->rb_node) && (rq_rb_key(req) != crq->rb_key)) {		struct cfq_queue *cfqq = crq->cfq_queue;		cfq_del_crq_rb(cfqq, crq);		cfq_add_crq_rb(cfqd, cfqq, crq);	}	q->last_merge = req;}static voidcfq_merged_requests(request_queue_t *q, struct request *req,		    struct request *next){	cfq_merged_request(q, req);	cfq_remove_request(q, next);}static voidcfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq,		  struct cfq_rq *crq){	struct list_head *head = cfqd->dispatch, *entry = head;	struct request *__rq;	cfq_del_crq_rb(cfqq, crq);	cfq_remove_merge_hints(cfqd->queue, crq);	if (!list_empty(head)) {		__rq = list_entry_rq(head->next);		if (crq->request->sector < __rq->sector) {			entry = head->prev;			goto link;		}	}	while ((entry = entry->prev) != head) {		__rq = list_entry_rq(entry);		if (crq->request->sector <= __rq->sector)			break;	}link:	list_add_tail(&crq->request->queuelist, entry);}static inline void__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd,			struct cfq_queue *cfqq){	struct cfq_rq *crq = rb_entry_crq(rb_first(&cfqq->sort_list));	cfq_dispatch_sort(cfqd, cfqq, crq);}static int cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd){	struct cfq_queue *cfqq;	struct list_head *entry, *tmp;	int ret, queued, good_queues;	if (list_empty(&cfqd->rr_list))		return 0;	queued = ret = 0;restart:	good_queues = 0;	list_for_each_safe(entry, tmp, &cfqd->rr_list) {		cfqq = list_entry_cfqq(cfqd->rr_list.next);		BUG_ON(RB_EMPTY(&cfqq->sort_list));		__cfq_dispatch_requests(q, cfqd, cfqq);		if (RB_EMPTY(&cfqq->sort_list))			cfq_put_queue(cfqd, cfqq);		else			good_queues++;		queued++;		ret = 1;	}	if ((queued < cfqd->cfq_quantum) && good_queues)		goto restart;	return ret;}static struct request *cfq_next_request(request_queue_t *q){	struct cfq_data *cfqd = q->elevator.elevator_data;	struct request *rq;	if (!list_empty(cfqd->dispatch)) {		struct cfq_rq *crq;dispatch:		rq = list_entry_rq(cfqd->dispatch->next);		crq = RQ_DATA(rq);		if (crq)			cfq_remove_merge_hints(q, crq);		return rq;	}	if (cfq_dispatch_requests(q, cfqd))		goto dispatch;	return NULL;}static inline struct cfq_queue *__cfq_find_cfq_hash(struct cfq_data *cfqd, int pid, const int hashval){	struct list_head *hash_list = &cfqd->cfq_hash[hashval];	struct list_head *entry;	list_for_each(entry, hash_list) {		struct cfq_queue *__cfqq = list_entry_qhash(entry);		if (__cfqq->pid == pid)			return __cfqq;	}	return NULL;}static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid){	const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?