📄 cfq-iosched.c
字号:
return crq; } } return NULL;}/* * Scale schedule slice based on io priority. Use the sync time slice only * if a queue is marked sync and has sync io queued. A sync queue with async * io only, should not get full sync slice length. */static inline intcfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq){ const int base_slice = cfqd->cfq_slice[cfq_cfqq_sync(cfqq)]; WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); return base_slice + (base_slice/CFQ_SLICE_SCALE * (4 - cfqq->ioprio));}static inline voidcfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq){ cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies;}static inline intcfq_prio_to_maxrq(struct cfq_data *cfqd, struct cfq_queue *cfqq){ const int base_rq = cfqd->cfq_slice_async_rq; WARN_ON(cfqq->ioprio >= IOPRIO_BE_NR); return 2 * (base_rq + base_rq * (CFQ_PRIO_LISTS - 1 - cfqq->ioprio));}/* * get next queue for service */static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd, int force){ unsigned long now = jiffies; struct cfq_queue *cfqq; cfqq = cfqd->active_queue; if (!cfqq) goto new_queue; if (cfq_cfqq_expired(cfqq)) goto new_queue; /* * slice has expired */ if (!cfq_cfqq_must_dispatch(cfqq) && time_after(now, cfqq->slice_end)) goto expire; /* * if queue has requests, dispatch one. if not, check if * enough slice is left to wait for one */ if (!RB_EMPTY(&cfqq->sort_list)) goto keep_queue; else if (!force && cfq_cfqq_class_sync(cfqq) && time_before(now, cfqq->slice_end)) { if (cfq_arm_slice_timer(cfqd, cfqq)) return NULL; }expire: cfq_slice_expired(cfqd, 0);new_queue: cfqq = cfq_set_active_queue(cfqd);keep_queue: return cfqq;}static int__cfq_dispatch_requests(struct cfq_data *cfqd, struct cfq_queue *cfqq, int max_dispatch){ int dispatched = 0; BUG_ON(RB_EMPTY(&cfqq->sort_list)); do { struct cfq_rq *crq; /* * follow expired path, else get first next available */ if ((crq = cfq_check_fifo(cfqq)) == NULL) crq = cfqq->next_crq; /* * finally, insert request into driver dispatch list */ cfq_dispatch_sort(cfqd->queue, crq); cfqd->dispatch_slice++; dispatched++; if (!cfqd->active_cic) { atomic_inc(&crq->io_context->ioc->refcount); cfqd->active_cic = crq->io_context; } if (RB_EMPTY(&cfqq->sort_list)) break; } while (dispatched < max_dispatch); /* * if slice end isn't set yet, set it. if at least one request was * sync, use the sync time slice value */ if (!cfqq->slice_end) cfq_set_prio_slice(cfqd, cfqq); /* * expire an async queue immediately if it has used up its slice. idle * queue always expire after 1 dispatch round. */ if ((!cfq_cfqq_sync(cfqq) && cfqd->dispatch_slice >= cfq_prio_to_maxrq(cfqd, cfqq)) || cfq_class_idle(cfqq)) cfq_slice_expired(cfqd, 0); return dispatched;}static intcfq_dispatch_requests(request_queue_t *q, int max_dispatch, int force){ struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq; if (!cfqd->busy_queues) return 0; cfqq = cfq_select_queue(cfqd, force); if (cfqq) { cfq_clear_cfqq_must_dispatch(cfqq); cfq_clear_cfqq_wait_request(cfqq); del_timer(&cfqd->idle_slice_timer); if (cfq_class_idle(cfqq)) max_dispatch = 1; return __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); } return 0;}static inline void cfq_account_dispatch(struct cfq_rq *crq){ struct cfq_queue *cfqq = crq->cfq_queue; struct cfq_data *cfqd = cfqq->cfqd; if (unlikely(!blk_fs_request(crq->request))) return; /* * accounted bit is necessary since some drivers will call * elv_next_request() many times for the same request (eg ide) */ if (cfq_crq_in_driver(crq)) return; cfq_mark_crq_in_driver(crq); cfqd->rq_in_driver++;}static inline voidcfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq){ struct cfq_data *cfqd = cfqq->cfqd; unsigned long now; if (!cfq_crq_in_driver(crq)) return; now = jiffies; WARN_ON(!cfqd->rq_in_driver); cfqd->rq_in_driver--; if (!cfq_class_idle(cfqq)) cfqd->last_end_request = now; if (!cfq_cfqq_dispatched(cfqq)) { if (cfq_cfqq_on_rr(cfqq)) { cfqq->service_last = now; cfq_resort_rr_list(cfqq, 0); } if (cfq_cfqq_expired(cfqq)) { __cfq_slice_expired(cfqd, cfqq, 0); cfq_schedule_dispatch(cfqd); } } if (cfq_crq_is_sync(crq)) crq->io_context->last_end_request = now;}static struct request *cfq_next_request(request_queue_t *q){ struct cfq_data *cfqd = q->elevator->elevator_data; struct request *rq; if (!list_empty(&q->queue_head)) { struct cfq_rq *crq;dispatch: rq = list_entry_rq(q->queue_head.next); crq = RQ_DATA(rq); if (crq) { struct cfq_queue *cfqq = crq->cfq_queue; /* * if idle window is disabled, allow queue buildup */ if (!cfq_crq_in_driver(crq) && !cfq_cfqq_idle_window(cfqq) && !blk_barrier_rq(rq) && cfqd->rq_in_driver >= cfqd->cfq_max_depth) return NULL; cfq_remove_merge_hints(q, crq); cfq_account_dispatch(crq); } return rq; } if (cfq_dispatch_requests(q, cfqd->cfq_quantum, 0)) goto dispatch; return NULL;}/* * task holds one reference to the queue, dropped when task exits. each crq * in-flight on this queue also holds a reference, dropped when crq is freed. * * queue lock must be held here. */static void cfq_put_queue(struct cfq_queue *cfqq){ struct cfq_data *cfqd = cfqq->cfqd; BUG_ON(atomic_read(&cfqq->ref) <= 0); if (!atomic_dec_and_test(&cfqq->ref)) return; BUG_ON(rb_first(&cfqq->sort_list)); BUG_ON(cfqq->allocated[READ] + cfqq->allocated[WRITE]); BUG_ON(cfq_cfqq_on_rr(cfqq)); if (unlikely(cfqd->active_queue == cfqq)) { __cfq_slice_expired(cfqd, cfqq, 0); cfq_schedule_dispatch(cfqd); } cfq_put_cfqd(cfqq->cfqd); /* * it's on the empty list and still hashed */ list_del(&cfqq->cfq_list); hlist_del(&cfqq->cfq_hash); kmem_cache_free(cfq_pool, cfqq);}static inline struct cfq_queue *__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned int prio, const int hashval){ struct hlist_head *hash_list = &cfqd->cfq_hash[hashval]; struct hlist_node *entry, *next; hlist_for_each_safe(entry, next, hash_list) { struct cfq_queue *__cfqq = list_entry_qhash(entry); const unsigned short __p = IOPRIO_PRIO_VALUE(__cfqq->ioprio_class, __cfqq->ioprio); if (__cfqq->key == key && (__p == prio || prio == CFQ_KEY_ANY)) return __cfqq; } return NULL;}static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned int key, unsigned short prio){ return __cfq_find_cfq_hash(cfqd, key, prio, hash_long(key, CFQ_QHASH_SHIFT));}static void cfq_free_io_context(struct cfq_io_context *cic){ struct cfq_io_context *__cic; struct list_head *entry, *next; list_for_each_safe(entry, next, &cic->list) { __cic = list_entry(entry, struct cfq_io_context, list); kmem_cache_free(cfq_ioc_pool, __cic); } kmem_cache_free(cfq_ioc_pool, cic);}/* * Called with interrupts disabled */static void cfq_exit_single_io_context(struct cfq_io_context *cic){ struct cfq_data *cfqd = cic->cfqq->cfqd; request_queue_t *q = cfqd->queue; WARN_ON(!irqs_disabled()); spin_lock(q->queue_lock); if (unlikely(cic->cfqq == cfqd->active_queue)) { __cfq_slice_expired(cfqd, cic->cfqq, 0); cfq_schedule_dispatch(cfqd); } cfq_put_queue(cic->cfqq); cic->cfqq = NULL; spin_unlock(q->queue_lock);}/* * Another task may update the task cic list, if it is doing a queue lookup * on its behalf. cfq_cic_lock excludes such concurrent updates */static void cfq_exit_io_context(struct cfq_io_context *cic){ struct cfq_io_context *__cic; struct list_head *entry; unsigned long flags; local_irq_save(flags); /* * put the reference this task is holding to the various queues */ list_for_each(entry, &cic->list) { __cic = list_entry(entry, struct cfq_io_context, list); cfq_exit_single_io_context(__cic); } cfq_exit_single_io_context(cic); local_irq_restore(flags);}static struct cfq_io_context *cfq_alloc_io_context(struct cfq_data *cfqd, int gfp_mask){ struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_mask); if (cic) { INIT_LIST_HEAD(&cic->list); cic->cfqq = NULL; cic->key = NULL; cic->last_end_request = jiffies; cic->ttime_total = 0; cic->ttime_samples = 0; cic->ttime_mean = 0; cic->dtor = cfq_free_io_context; cic->exit = cfq_exit_io_context; } return cic;}static void cfq_init_prio_data(struct cfq_queue *cfqq){ struct task_struct *tsk = current; int ioprio_class; if (!cfq_cfqq_prio_changed(cfqq)) return; ioprio_class = IOPRIO_PRIO_CLASS(tsk->ioprio); switch (ioprio_class) { default: printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class); case IOPRIO_CLASS_NONE: /* * no prio set, place us in the middle of the BE classes */ cfqq->ioprio = task_nice_ioprio(tsk); cfqq->ioprio_class = IOPRIO_CLASS_BE; break; case IOPRIO_CLASS_RT: cfqq->ioprio = task_ioprio(tsk); cfqq->ioprio_class = IOPRIO_CLASS_RT; break; case IOPRIO_CLASS_BE: cfqq->ioprio = task_ioprio(tsk); cfqq->ioprio_class = IOPRIO_CLASS_BE; break; case IOPRIO_CLASS_IDLE: cfqq->ioprio_class = IOPRIO_CLASS_IDLE; cfqq->ioprio = 7; cfq_clear_cfqq_idle_window(cfqq); break; } /* * keep track of original prio settings in case we have to temporarily * elevate the priority of this queue */ cfqq->org_ioprio = cfqq->ioprio; cfqq->org_ioprio_class = cfqq->ioprio_class; if (cfq_cfqq_on_rr(cfqq)) cfq_resort_rr_list(cfqq, 0); cfq_clear_cfqq_prio_changed(cfqq);}static inline void changed_ioprio(struct cfq_queue *cfqq){ if (cfqq) { struct cfq_data *cfqd = cfqq->cfqd; spin_lock(cfqd->queue->queue_lock); cfq_mark_cfqq_prio_changed(cfqq); cfq_init_prio_data(cfqq); spin_unlock(cfqd->queue->queue_lock); }}/* * callback from sys_ioprio_set, irqs are disabled */static int cfq_ioc_set_ioprio(struct io_context *ioc, unsigned int ioprio){ struct cfq_io_context *cic = ioc->cic; changed_ioprio(cic->cfqq); list_for_each_entry(cic, &cic->list, list) changed_ioprio(cic->cfqq); return 0;}static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, unsigned int key, unsigned short ioprio, int gfp_mask){ const int hashval = hash_long(key, CFQ_QHASH_SHIFT); struct cfq_queue *cfqq, *new_cfqq = NULL;retry: cfqq = __cfq_find_cfq_hash(cfqd, key, ioprio, hashval); if (!cfqq) { if (new_cfqq) { cfqq = new_cfqq; new_cfqq = NULL; } else if (gfp_mask & __GFP_WAIT) { spin_unlock_irq(cfqd->queue->queue_lock); new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); spin_lock_irq(cfqd->queue->queue_lock); goto retry; } else { cfqq = kmem_cache_alloc(cfq_pool, gfp_mask); if (!cfqq) goto out; } memset(cfqq, 0, sizeof(*cfqq)); INIT_HLIST_NODE(&cfqq->cfq_hash); INIT_LIST_HEAD(&cfqq->cfq_list); RB_CLEAR_ROOT(&cfqq->sort_list); INIT_LIST_HEAD(&cfqq->fifo); cfqq->key = key; hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); atomic_set(&cfqq->ref, 0); cfqq->cfqd = cfqd; atomic_inc(&cfqd->ref); cfqq->service_last = 0; /* * set ->slice_left to allow preemption for a new process */ cfqq->slice_left = 2 * cfqd->cfq_slice_idle; cfq_mark_cfqq_idle_window(cfqq); cfq_mark_cfqq_prio_changed(cfqq); cfq_init_prio_data(cfqq); } if (new_cfqq) kmem_cache_free(cfq_pool, new_cfqq); atomic_inc(&cfqq->ref);out: WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq); return cfqq;}/* * Setup general io context and cfq io context. There can be several cfq * io contexts per general io context, if this process is doing io to more * than one device managed by cfq. Note that caller is holding a reference to * cfqq, so we don't need to worry about it disappearing */static struct cfq_io_context *cfq_get_io_context(struct cfq_data *cfqd, pid_t pid, int gfp_mask){ struct io_context *ioc = NULL; struct cfq_io_context *cic; might_sleep_if(gfp_mask & __GFP_WAIT); ioc = get_io_context(gfp_mask); if (!ioc) return NULL; if ((cic = ioc->cic) == NULL) { cic = cfq_alloc_io_context(cfqd, gfp_mask);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -