📄 ll_rw_blk.c
字号:
while (!rq) { DEFINE_WAIT(wait); struct request_list *rl = &q->rq; prepare_to_wait_exclusive(&rl->wait[rw], &wait, TASK_UNINTERRUPTIBLE); rq = get_request(q, rw, bio, GFP_NOIO); if (!rq) { struct io_context *ioc; __generic_unplug_device(q); spin_unlock_irq(q->queue_lock); io_schedule(); /* * After sleeping, we become a "batching" process and * will be able to allocate at least one request, and * up to a big batch of them for a small period time. * See ioc_batching, ioc_set_batching */ ioc = current_io_context(GFP_NOIO); ioc_set_batching(q, ioc); spin_lock_irq(q->queue_lock); } finish_wait(&rl->wait[rw], &wait); } return rq;}struct request *blk_get_request(request_queue_t *q, int rw, int gfp_mask){ struct request *rq; BUG_ON(rw != READ && rw != WRITE); spin_lock_irq(q->queue_lock); if (gfp_mask & __GFP_WAIT) { rq = get_request_wait(q, rw, NULL); } else { rq = get_request(q, rw, NULL, gfp_mask); if (!rq) spin_unlock_irq(q->queue_lock); } /* q->queue_lock is unlocked at this point */ return rq;}EXPORT_SYMBOL(blk_get_request);/** * blk_requeue_request - put a request back on queue * @q: request queue where request should be inserted * @rq: request to be inserted * * Description: * Drivers often keep queueing requests until the hardware cannot accept * more, when that condition happens we need to put the request back * on the queue. Must be called with queue lock held. */void blk_requeue_request(request_queue_t *q, struct request *rq){ if (blk_rq_tagged(rq)) blk_queue_end_tag(q, rq); elv_requeue_request(q, rq);}EXPORT_SYMBOL(blk_requeue_request);/** * blk_insert_request - insert a special request in to a request queue * @q: request queue where request should be inserted * @rq: request to be inserted * @at_head: insert request at head or tail of queue * @data: private data * * Description: * Many block devices need to execute commands asynchronously, so they don't * block the whole kernel from preemption during request execution. This is * accomplished normally by inserting aritficial requests tagged as * REQ_SPECIAL in to the corresponding request queue, and letting them be * scheduled for actual execution by the request queue. * * We have the option of inserting the head or the tail of the queue. * Typically we use the tail for new ioctls and so forth. We use the head * of the queue for things like a QUEUE_FULL message from a device, or a * host that is unable to accept a particular command. */void blk_insert_request(request_queue_t *q, struct request *rq, int at_head, void *data){ int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK; unsigned long flags; /* * tell I/O scheduler that this isn't a regular read/write (ie it * must not attempt merges on this) and that it acts as a soft * barrier */ rq->flags |= REQ_SPECIAL | REQ_SOFTBARRIER; rq->special = data; spin_lock_irqsave(q->queue_lock, flags); /* * If command is tagged, release the tag */ if (blk_rq_tagged(rq)) blk_queue_end_tag(q, rq); drive_stat_acct(rq, rq->nr_sectors, 1); __elv_add_request(q, rq, where, 0); if (blk_queue_plugged(q)) __generic_unplug_device(q); else q->request_fn(q); spin_unlock_irqrestore(q->queue_lock, flags);}EXPORT_SYMBOL(blk_insert_request);/** * blk_rq_map_user - map user data to a request, for REQ_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request structure to fill * @ubuf: the user buffer * @len: length of user data * * Description: * Data will be mapped directly for zero copy io, if possible. Otherwise * a kernel bounce buffer is used. * * A matching blk_rq_unmap_user() must be issued at the end of io, while * still in process context. * * Note: The mapped bio may need to be bounced through blk_queue_bounce() * before being submitted to the device, as pages mapped may be out of * reach. It's the callers responsibility to make sure this happens. The * original bio must be passed back in to blk_rq_unmap_user() for proper * unmapping. */int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf, unsigned int len){ unsigned long uaddr; struct bio *bio; int reading; if (len > (q->max_sectors << 9)) return -EINVAL; if (!len || !ubuf) return -EINVAL; reading = rq_data_dir(rq) == READ; /* * if alignment requirement is satisfied, map in user pages for * direct dma. else, set up kernel bounce buffers */ uaddr = (unsigned long) ubuf; if (!(uaddr & queue_dma_alignment(q)) && !(len & queue_dma_alignment(q))) bio = bio_map_user(q, NULL, uaddr, len, reading); else bio = bio_copy_user(q, uaddr, len, reading); if (!IS_ERR(bio)) { rq->bio = rq->biotail = bio; blk_rq_bio_prep(q, rq, bio); rq->buffer = rq->data = NULL; rq->data_len = len; return 0; } /* * bio is the err-ptr */ return PTR_ERR(bio);}EXPORT_SYMBOL(blk_rq_map_user);/** * blk_rq_map_user_iov - map user data to a request, for REQ_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request to map data to * @iov: pointer to the iovec * @iov_count: number of elements in the iovec * * Description: * Data will be mapped directly for zero copy io, if possible. Otherwise * a kernel bounce buffer is used. * * A matching blk_rq_unmap_user() must be issued at the end of io, while * still in process context. * * Note: The mapped bio may need to be bounced through blk_queue_bounce() * before being submitted to the device, as pages mapped may be out of * reach. It's the callers responsibility to make sure this happens. The * original bio must be passed back in to blk_rq_unmap_user() for proper * unmapping. */int blk_rq_map_user_iov(request_queue_t *q, struct request *rq, struct sg_iovec *iov, int iov_count){ struct bio *bio; if (!iov || iov_count <= 0) return -EINVAL; /* we don't allow misaligned data like bio_map_user() does. If the * user is using sg, they're expected to know the alignment constraints * and respect them accordingly */ bio = bio_map_user_iov(q, NULL, iov, iov_count, rq_data_dir(rq)== READ); if (IS_ERR(bio)) return PTR_ERR(bio); rq->bio = rq->biotail = bio; blk_rq_bio_prep(q, rq, bio); rq->buffer = rq->data = NULL; rq->data_len = bio->bi_size; return 0;}EXPORT_SYMBOL(blk_rq_map_user_iov);/** * blk_rq_unmap_user - unmap a request with user data * @bio: bio to be unmapped * @ulen: length of user buffer * * Description: * Unmap a bio previously mapped by blk_rq_map_user(). */int blk_rq_unmap_user(struct bio *bio, unsigned int ulen){ int ret = 0; if (bio) { if (bio_flagged(bio, BIO_USER_MAPPED)) bio_unmap_user(bio); else ret = bio_uncopy_user(bio); } return 0;}EXPORT_SYMBOL(blk_rq_unmap_user);/** * blk_rq_map_kern - map kernel data to a request, for REQ_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request to fill * @kbuf: the kernel buffer * @len: length of user data * @gfp_mask: memory allocation flags */int blk_rq_map_kern(request_queue_t *q, struct request *rq, void *kbuf, unsigned int len, unsigned int gfp_mask){ struct bio *bio; if (len > (q->max_sectors << 9)) return -EINVAL; if (!len || !kbuf) return -EINVAL; bio = bio_map_kern(q, kbuf, len, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); if (rq_data_dir(rq) == WRITE) bio->bi_rw |= (1 << BIO_RW); rq->bio = rq->biotail = bio; blk_rq_bio_prep(q, rq, bio); rq->buffer = rq->data = NULL; rq->data_len = len; return 0;}EXPORT_SYMBOL(blk_rq_map_kern);/** * blk_execute_rq_nowait - insert a request into queue for execution * @q: queue to insert the request in * @bd_disk: matching gendisk * @rq: request to insert * @at_head: insert request at head or tail of queue * @done: I/O completion handler * * Description: * Insert a fully prepared request at the back of the io scheduler queue * for execution. Don't wait for completion. */void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk, struct request *rq, int at_head, void (*done)(struct request *)){ int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK; rq->rq_disk = bd_disk; rq->flags |= REQ_NOMERGE; rq->end_io = done; elv_add_request(q, rq, where, 1); generic_unplug_device(q);}/** * blk_execute_rq - insert a request into queue for execution * @q: queue to insert the request in * @bd_disk: matching gendisk * @rq: request to insert * @at_head: insert request at head or tail of queue * * Description: * Insert a fully prepared request at the back of the io scheduler queue * for execution and wait for completion. */int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk, struct request *rq, int at_head){ DECLARE_COMPLETION(wait); char sense[SCSI_SENSE_BUFFERSIZE]; int err = 0; /* * we need an extra reference to the request, so we can look at * it after io completion */ rq->ref_count++; if (!rq->sense) { memset(sense, 0, sizeof(sense)); rq->sense = sense; rq->sense_len = 0; } rq->waiting = &wait; blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq); wait_for_completion(&wait); rq->waiting = NULL; if (rq->errors) err = -EIO; return err;}EXPORT_SYMBOL(blk_execute_rq);/** * blkdev_issue_flush - queue a flush * @bdev: blockdev to issue flush for * @error_sector: error sector * * Description: * Issue a flush for the block device in question. Caller can supply * room for storing the error offset in case of a flush error, if they * wish to. Caller must run wait_for_completion() on its own. */int blkdev_issue_flush(struct block_device *bdev, sector_t *error_sector){ request_queue_t *q; if (bdev->bd_disk == NULL) return -ENXIO; q = bdev_get_queue(bdev); if (!q) return -ENXIO; if (!q->issue_flush_fn) return -EOPNOTSUPP; return q->issue_flush_fn(q, bdev->bd_disk, error_sector);}EXPORT_SYMBOL(blkdev_issue_flush);static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io){ int rw = rq_data_dir(rq); if (!blk_fs_request(rq) || !rq->rq_disk) return; if (rw == READ) { __disk_stat_add(rq->rq_disk, read_sectors, nr_sectors); if (!new_io) __disk_stat_inc(rq->rq_disk, read_merges); } else if (rw == WRITE) { __disk_stat_add(rq->rq_disk, write_sectors, nr_sectors); if (!new_io) __disk_stat_inc(rq->rq_disk, write_merges); } if (new_io) { disk_round_stats(rq->rq_disk); rq->rq_disk->in_flight++; }}/* * add-request adds a request to the linked list. * queue lock is held and interrupts disabled, as we muck with the * request queue list. */static inline void add_request(request_queue_t * q, struct request * req){ drive_stat_acct(req, req->nr_sectors, 1); if (q->activity_fn) q->activity_fn(q->activity_data, rq_data_dir(req)); /* * elevator indicated where it wants this request to be * inserted at elevator_merge time */ __elv_add_request(q, req, ELEVATOR_INSERT_SORT, 0);} /* * disk_round_stats() - Round off the performance stats on a struct * disk_stats. * * The average IO queue length and utilisation statistics are maintained * by observing the current state of the queue length and the amount of * time it has been in this state for. * * Normally, that accounting is done on IO completion, but that can result * in more than a second's worth of IO being accounted for within any one * second, leading to >100% utilisation. To deal with that, we call this * function to do a round-off before returning the results when reading * /proc/diskstats. This accounts immediately for all queue usage up to * the current jiffies and restarts the counters again. */void disk_round_stats(struct gendisk *disk){ unsigned long now = jiffies; __disk_stat_add(disk, time_in_queue, disk->in_flight * (now - disk->stamp)); disk->stamp = now; if (disk->in_flight) __disk_stat_add(disk, io_ticks, (now - disk->stamp_idle)); disk->stamp_idle = now;}/* * queue lock must be held */static void __blk_put_request(request_queue_t *q, struct request *req){ struct request_list *rl = req->rl; if (unlikely(!q)) return; if (unlikely(--req->ref_count)) return; req->rq_status = RQ_INACTIVE; req->rl = NULL; /* * Request may not have originated from ll_rw_blk. if not, * it didn't come out of our reserved rq pools */ if (rl) { int rw = rq_data_dir(req); elv_completed_request(q, req); BUG_ON(!list_empty(&req->queuelist)); blk_free_request(q, req); freed_request(q, rw); }}void blk_put_request(struct request *req){ /* * if req->rl isn't set, this request didnt originate from the * block layer, so it's safe to just disregard it */ if (req->rl) { unsigned lo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -