linux内核之块设备二---真正派发请求request
2016-07-23 13:09
701 查看
触发请求request派发的时机:
重新申请一个请求失败时
将一个带有unplug标记的bio添加到请求队列时
将请求添加调度器的调度队列,达到了unplug阈值时
unplug定时时间到,周期性
前三个调用__generic_unplug_device,最后一个调用generic_unplug_device
互斥机制:加锁,请求队列的锁
在初始化请求队列时,默认的块设备的请求下发接口设置为scsi_request_fn
/*真正派发请求request*/
重新申请一个请求失败时
将一个带有unplug标记的bio添加到请求队列时
将请求添加调度器的调度队列,达到了unplug阈值时
unplug定时时间到,周期性
前三个调用__generic_unplug_device,最后一个调用generic_unplug_device
互斥机制:加锁,请求队列的锁
在初始化请求队列时,默认的块设备的请求下发接口设置为scsi_request_fn
/*真正派发请求request*/
void generic_unplug_device(struct request_queue *q) { if (blk_queue_plugged(q)) { spin_lock_irq(q->queue_lock); __generic_unplug_device(q); spin_unlock_irq(q->queue_lock); } } /* * remove the plug and let it rip.. */ void __generic_unplug_device(struct request_queue *q) { if (unlikely(blk_queue_stopped(q))) return; if (!blk_remove_plug(q) && !blk_queue_nonrot(q)) return; q->request_fn(q); } /* * remove the queue from the plugged list, if present. called with * queue lock held and interrupts disabled. */ int blk_remove_plug(struct request_queue *q) { WARN_ON(!irqs_disabled()); if (!queue_flag_test_and_clear(QUEUE_FLAG_PLUGGED, q)) return 0; del_timer(&q->unplug_timer); return 1; } /* * Function: scsi_request_fn() * * Purpose: Main strategy routine for SCSI. * * Arguments: q - Pointer to actual queue. * * Returns: Nothing * * Lock status: IO request lock assumed to be held when called. */ static void scsi_request_fn(struct request_queue *q) { struct scsi_device *sdev = q->queuedata; struct Scsi_Host *shost; struct scsi_cmnd *cmd; struct request *req; if(!get_device(&sdev->sdev_gendev)) /* We must be tearing the block queue down already */ return; /* * To start with, we keep looping until the queue is empty, or until * the host is no longer able to accept any more requests. */ shost = sdev->host; while (!blk_queue_plugged(q)) { int rtn; /* * get next queueable request. We do this early to make sure * that the request is fully prepared even if we cannot * accept it. */ req = blk_peek_request(q); //从请求队列的派发队列获取一个请求,并为请求准备cmd if (!req || !scsi_dev_queue_ready(q, sdev)) break; if (unlikely(!scsi_device_online(sdev))) { sdev_printk(KERN_ERR, sdev, "rejecting I/O to offline device\n"); scsi_kill_request(req, q); continue; } /* * Remove the request from the request list. 从派发队列移除该请求,并为该请求添加一个定时器 */ if (!(blk_queue_tagged(q) && !blk_queue_start_tag(q, req))) blk_start_request(req); sdev->device_busy++; spin_unlock(q->queue_lock); cmd = req->special; if (unlikely(cmd == NULL)) { printk(KERN_CRIT "impossible request in %s.\n" "please mail a stack trace to " "linux-scsi@vger.kernel.org\n", __func__); blk_dump_rq_flags(req, "foo"); BUG(); } spin_lock(shost->host_lock); /* * We hit this when the driver is using a host wide * tag map. For device level tag maps the queue_depth check * in the device ready fn would prevent us from trying * to allocate a tag. Since the map is a shared host resource * we add the dev to the starved list so it eventually gets * a run when a tag is freed. */ if (blk_queue_tagged(q) && !blk_rq_tagged(req)) { if (list_empty(&sdev->starved_entry)) list_add_tail(&sdev->starved_entry, &shost->starved_list); goto not_ready; } if (!scsi_target_queue_ready(shost, sdev)) goto not_ready; if (!scsi_host_queue_ready(q, shost, sdev)) goto not_ready; scsi_target(sdev)->target_busy++; shost->host_busy++; /* * XXX(hch): This is rather suboptimal, scsi_dispatch_cmd will * take the lock again. */ spin_unlock_irq(shost->host_lock); /* * Finally, initialize any error handling parameters, and set up * the timers for timeouts. */ scsi_init_cmd_errh(cmd); //初始化cmd /* * Dispatch the command to the low-level driver. */ rtn = scsi_dispatch_cmd(cmd); //将cmd派发到底层的驱动 spin_lock_irq(q->queue_lock); if(rtn) { /* we're refusing the command; because of * the way locks get dropped, we need to * check here if plugging is required */ if(sdev->device_busy == 0) blk_plug_device(q); break; } } goto out; not_ready: spin_unlock_irq(shost->host_lock); /* * lock q, handle tag, requeue req, and decrement device_busy. We * must return with queue_lock held. * * Decrementing device_busy without checking it is OK, as all such * cases (host limits or settings) should run the queue at some * later time. */ spin_lock_irq(q->queue_lock); blk_requeue_request(q, req); sdev->device_busy--; if(sdev->device_busy == 0) blk_plug_device(q); out: /* must be careful here...if we trigger the ->remove() function * we cannot be holding the q lock */ spin_unlock_irq(q->queue_lock); put_device(&sdev->sdev_gendev); spin_lock_irq(q->queue_lock); } struct request *blk_peek_request(struct request_queue *q) { struct request *rq; int ret; while ((rq = __elv_next_request(q)) != NULL) { if (!(rq->cmd_flags & REQ_STARTED)) { /* * This is the first time the device driver * sees this request (possibly after * requeueing). Notify IO scheduler. */ if (rq->cmd_flags & REQ_SORTED) elv_activate_rq(q, rq); /* * just mark as started even if we don't start * it, a request that has been delayed should * not be passed by new incoming requests */ rq->cmd_flags |= REQ_STARTED; trace_block_rq_issue(q, rq); } if (!q->boundary_rq || q->boundary_rq == rq) { q->end_sector = rq_end_sector(rq); q->boundary_rq = NULL; } if (rq->cmd_flags & REQ_DONTPREP) break; if (q->dma_drain_size && blk_rq_bytes(rq)) { /* * make sure space for the drain appears we * know we can do this because max_hw_segments * has been adjusted to be one fewer than the * device can handle */ rq->nr_phys_segments++; } if (!q->prep_rq_fn) break; ret = q->prep_rq_fn(q, rq); //为请求准备cmd if (ret == BLKPREP_OK) { break; } else if (ret == BLKPREP_DEFER) { /* * the request may have been (partially) prepped. * we need to keep this request in the front to * avoid resource deadlock. REQ_STARTED will * prevent other fs requests from passing this one. */ if (q->dma_drain_size && blk_rq_bytes(rq) && !(rq->cmd_flags & REQ_DONTPREP)) { /* * remove the space for the drain we added * so that we don't add it again */ --rq->nr_phys_segments; } rq = NULL; break; } else if (ret == BLKPREP_KILL) { rq->cmd_flags |= REQ_QUIET; /* * Mark this request as started so we don't trigger * any debug logic in the end I/O path. */ blk_start_request(rq); __blk_end_request_all(rq, -EIO); } else { printk(KERN_ERR "%s: bad return=%d\n", __func__, ret); break; } } return rq; } /*获取下一个要处理的请求*/ static inline struct request *__elv_next_request(struct request_queue *q) { struct request *rq; while (1) { if (!list_empty(&q->queue_head)) { //1、如果派发队列由请求,直接从派发队列头获取请求 rq = list_entry_rq(q->queue_head.next); return rq; } /* * Flush request is running and flush request isn't queueable * in the drive, we can hold the queue till flush request is * finished. Even we don't do this, driver can't dispatch next * requests and will requeue them. And this can improve * throughput too. For example, we have request flush1, write1, * flush 2. flush1 is dispatched, then queue is hold, write1 * isn't inserted to queue. After flush1 is finished, flush2 * will be dispatched. Since disk cache is already clean, * flush2 will be finished very soon, so looks like flush2 is * folded to flush1. * Since the queue is hold, a flag is set to indicate the queue * should be restarted later. Please see flush_end_io() for * details. */ if (q->flush_pending_idx != q->flush_running_idx && !queue_flush_queueable(q)) { q->flush_queue_delayed = 1; return NULL; } if (unlikely(blk_queue_dead(q)) || !q->elevator->ops->elevator_dispatch_fn(q, 0)) //2、如果派发队列没有请求,则调用调度器的派发请求回调,让调度器派发请求到派发队列后处理 return NULL; } } void blk_start_request(struct request *req) { blk_dequeue_request(req); /* * We are now handing the request to the hardware, initialize * resid_len to full count and add the timeout handler. */ req->resid_len = blk_rq_bytes(req); if (unlikely(blk_bidi_rq(req))) req->next_rq->resid_len = blk_rq_bytes(req->next_rq); blk_add_timer(req); }
相关文章推荐
- Linux socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- 神器SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程