diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/Kconfig | 2 | ||||
-rw-r--r-- | block/as-iosched.c | 144 | ||||
-rw-r--r-- | block/cfq-iosched.c | 16 | ||||
-rw-r--r-- | block/deadline-iosched.c | 8 | ||||
-rw-r--r-- | block/elevator.c | 100 | ||||
-rw-r--r-- | block/genhd.c | 154 | ||||
-rw-r--r-- | block/ioctl.c | 24 | ||||
-rw-r--r-- | block/ll_rw_blk.c | 699 | ||||
-rw-r--r-- | block/scsi_ioctl.c | 63 |
9 files changed, 742 insertions, 468 deletions
diff --git a/block/Kconfig b/block/Kconfig index eb48edb80c1..377f6dd20e1 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -5,7 +5,7 @@ #for instance. config LBD bool "Support for Large Block Devices" - depends on X86 || (MIPS && 32BIT) || PPC32 || ARCH_S390_31 || SUPERH || UML + depends on X86 || (MIPS && 32BIT) || PPC32 || (S390 && !64BIT) || SUPERH || UML help Say Y here if you want to attach large (bigger than 2TB) discs to your machine, or if you want to have a raid or loopback device diff --git a/block/as-iosched.c b/block/as-iosched.c index 43fa2049568..8da3cf66894 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -182,6 +182,9 @@ struct as_rq { static kmem_cache_t *arq_pool; +static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq); +static void as_antic_stop(struct as_data *ad); + /* * IO Context helper functions */ @@ -370,7 +373,7 @@ static struct as_rq *as_find_first_arq(struct as_data *ad, int data_dir) * existing request against the same sector), which can happen when using * direct IO, then return the alias. */ -static struct as_rq *as_add_arq_rb(struct as_data *ad, struct as_rq *arq) +static struct as_rq *__as_add_arq_rb(struct as_data *ad, struct as_rq *arq) { struct rb_node **p = &ARQ_RB_ROOT(ad, arq)->rb_node; struct rb_node *parent = NULL; @@ -397,6 +400,16 @@ static struct as_rq *as_add_arq_rb(struct as_data *ad, struct as_rq *arq) return NULL; } +static void as_add_arq_rb(struct as_data *ad, struct as_rq *arq) +{ + struct as_rq *alias; + + while ((unlikely(alias = __as_add_arq_rb(ad, arq)))) { + as_move_to_dispatch(ad, alias); + as_antic_stop(ad); + } +} + static inline void as_del_arq_rb(struct as_data *ad, struct as_rq *arq) { if (!ON_RB(&arq->rb_node)) { @@ -1133,23 +1146,6 @@ static void as_move_to_dispatch(struct as_data *ad, struct as_rq *arq) /* * take it off the sort and fifo list, add to dispatch queue */ - while (!list_empty(&rq->queuelist)) { - struct request *__rq = list_entry_rq(rq->queuelist.next); - struct as_rq *__arq = RQ_DATA(__rq); - - list_del(&__rq->queuelist); - - elv_dispatch_add_tail(ad->q, __rq); - - if (__arq->io_context && __arq->io_context->aic) - atomic_inc(&__arq->io_context->aic->nr_dispatched); - - WARN_ON(__arq->state != AS_RQ_QUEUED); - __arq->state = AS_RQ_DISPATCHED; - - ad->nr_dispatched++; - } - as_remove_queued_request(ad->q, rq); WARN_ON(arq->state != AS_RQ_QUEUED); @@ -1326,49 +1322,12 @@ fifo_expired: } /* - * Add arq to a list behind alias - */ -static inline void -as_add_aliased_request(struct as_data *ad, struct as_rq *arq, - struct as_rq *alias) -{ - struct request *req = arq->request; - struct list_head *insert = alias->request->queuelist.prev; - - /* - * Transfer list of aliases - */ - while (!list_empty(&req->queuelist)) { - struct request *__rq = list_entry_rq(req->queuelist.next); - struct as_rq *__arq = RQ_DATA(__rq); - - list_move_tail(&__rq->queuelist, &alias->request->queuelist); - - WARN_ON(__arq->state != AS_RQ_QUEUED); - } - - /* - * Another request with the same start sector on the rbtree. - * Link this request to that sector. They are untangled in - * as_move_to_dispatch - */ - list_add(&arq->request->queuelist, insert); - - /* - * Don't want to have to handle merges. - */ - as_del_arq_hash(arq); - arq->request->flags |= REQ_NOMERGE; -} - -/* * add arq to rbtree and fifo */ static void as_add_request(request_queue_t *q, struct request *rq) { struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); - struct as_rq *alias; int data_dir; arq->state = AS_RQ_NEW; @@ -1387,33 +1346,17 @@ static void as_add_request(request_queue_t *q, struct request *rq) atomic_inc(&arq->io_context->aic->nr_queued); } - alias = as_add_arq_rb(ad, arq); - if (!alias) { - /* - * set expire time (only used for reads) and add to fifo list - */ - arq->expires = jiffies + ad->fifo_expire[data_dir]; - list_add_tail(&arq->fifo, &ad->fifo_list[data_dir]); + as_add_arq_rb(ad, arq); + if (rq_mergeable(arq->request)) + as_add_arq_hash(ad, arq); - if (rq_mergeable(arq->request)) - as_add_arq_hash(ad, arq); - as_update_arq(ad, arq); /* keep state machine up to date */ - - } else { - as_add_aliased_request(ad, arq, alias); - - /* - * have we been anticipating this request? - * or does it come from the same process as the one we are - * anticipating for? - */ - if (ad->antic_status == ANTIC_WAIT_REQ - || ad->antic_status == ANTIC_WAIT_NEXT) { - if (as_can_break_anticipation(ad, arq)) - as_antic_stop(ad); - } - } + /* + * set expire time (only used for reads) and add to fifo list + */ + arq->expires = jiffies + ad->fifo_expire[data_dir]; + list_add_tail(&arq->fifo, &ad->fifo_list[data_dir]); + as_update_arq(ad, arq); /* keep state machine up to date */ arq->state = AS_RQ_QUEUED; } @@ -1536,23 +1479,8 @@ static void as_merged_request(request_queue_t *q, struct request *req) * if the merge was a front merge, we need to reposition request */ if (rq_rb_key(req) != arq->rb_key) { - struct as_rq *alias, *next_arq = NULL; - - if (ad->next_arq[arq->is_sync] == arq) - next_arq = as_find_next_arq(ad, arq); - - /* - * Note! We should really be moving any old aliased requests - * off this request and try to insert them into the rbtree. We - * currently don't bother. Ditto the next function. - */ as_del_arq_rb(ad, arq); - if ((alias = as_add_arq_rb(ad, arq))) { - list_del_init(&arq->fifo); - as_add_aliased_request(ad, arq, alias); - if (next_arq) - ad->next_arq[arq->is_sync] = next_arq; - } + as_add_arq_rb(ad, arq); /* * Note! At this stage of this and the next function, our next * request may not be optimal - eg the request may have "grown" @@ -1579,18 +1507,8 @@ static void as_merged_requests(request_queue_t *q, struct request *req, as_add_arq_hash(ad, arq); if (rq_rb_key(req) != arq->rb_key) { - struct as_rq *alias, *next_arq = NULL; - - if (ad->next_arq[arq->is_sync] == arq) - next_arq = as_find_next_arq(ad, arq); - as_del_arq_rb(ad, arq); - if ((alias = as_add_arq_rb(ad, arq))) { - list_del_init(&arq->fifo); - as_add_aliased_request(ad, arq, alias); - if (next_arq) - ad->next_arq[arq->is_sync] = next_arq; - } + as_add_arq_rb(ad, arq); } /* @@ -1610,18 +1528,6 @@ static void as_merged_requests(request_queue_t *q, struct request *req, } /* - * Transfer list of aliases - */ - while (!list_empty(&next->queuelist)) { - struct request *__rq = list_entry_rq(next->queuelist.next); - struct as_rq *__arq = RQ_DATA(__rq); - - list_move_tail(&__rq->queuelist, &req->queuelist); - - WARN_ON(__arq->state != AS_RQ_QUEUED); - } - - /* * kill knowledge of next, this one is a goner */ as_remove_queued_request(q, next); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index ee0bb41694b..74fae2daf87 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -25,15 +25,15 @@ /* * tunables */ -static int cfq_quantum = 4; /* max queue in one round of service */ -static int cfq_queued = 8; /* minimum rq allocate limit per-queue*/ -static int cfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; -static int cfq_back_max = 16 * 1024; /* maximum backwards seek, in KiB */ -static int cfq_back_penalty = 2; /* penalty of a backwards seek */ +static const int cfq_quantum = 4; /* max queue in one round of service */ +static const int cfq_queued = 8; /* minimum rq allocate limit per-queue*/ +static const int cfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; +static const int cfq_back_max = 16 * 1024; /* maximum backwards seek, in KiB */ +static const int cfq_back_penalty = 2; /* penalty of a backwards seek */ -static int cfq_slice_sync = HZ / 10; +static const int cfq_slice_sync = HZ / 10; static int cfq_slice_async = HZ / 25; -static int cfq_slice_async_rq = 2; +static const int cfq_slice_async_rq = 2; static int cfq_slice_idle = HZ / 100; #define CFQ_IDLE_GRACE (HZ / 10) @@ -45,7 +45,7 @@ static int cfq_slice_idle = HZ / 100; /* * disable queueing at the driver/hardware level */ -static int cfq_max_depth = 2; +static const int cfq_max_depth = 2; /* * for the hash of cfqq inside the cfqd diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index 9cbec09e841..27e494b1bf9 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -19,10 +19,10 @@ /* * See Documentation/block/deadline-iosched.txt */ -static int read_expire = HZ / 2; /* max time before a read is submitted. */ -static int write_expire = 5 * HZ; /* ditto for writes, these limits are SOFT! */ -static int writes_starved = 2; /* max times reads can starve a write */ -static int fifo_batch = 16; /* # of sequential requests treated as one +static const int read_expire = HZ / 2; /* max time before a read is submitted. */ +static const int write_expire = 5 * HZ; /* ditto for writes, these limits are SOFT! */ +static const int writes_starved = 2; /* max times reads can starve a write */ +static const int fifo_batch = 16; /* # of sequential requests treated as one by the above parameters. For throughput. */ static const int deadline_hash_shift = 5; diff --git a/block/elevator.c b/block/elevator.c index 6c3fc8a10bf..c9f424d5399 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -64,7 +64,7 @@ inline int elv_rq_merge_ok(struct request *rq, struct bio *bio) } EXPORT_SYMBOL(elv_rq_merge_ok); -inline int elv_try_merge(struct request *__rq, struct bio *bio) +static inline int elv_try_merge(struct request *__rq, struct bio *bio) { int ret = ELEVATOR_NO_MERGE; @@ -80,7 +80,6 @@ inline int elv_try_merge(struct request *__rq, struct bio *bio) return ret; } -EXPORT_SYMBOL(elv_try_merge); static struct elevator_type *elevator_find(const char *name) { @@ -150,13 +149,20 @@ static void elevator_setup_default(void) if (!chosen_elevator[0]) strcpy(chosen_elevator, CONFIG_DEFAULT_IOSCHED); + /* + * Be backwards-compatible with previous kernels, so users + * won't get the wrong elevator. + */ + if (!strcmp(chosen_elevator, "as")) + strcpy(chosen_elevator, "anticipatory"); + /* - * If the given scheduler is not available, fall back to no-op. + * If the given scheduler is not available, fall back to the default */ if ((e = elevator_find(chosen_elevator))) elevator_put(e); else - strcpy(chosen_elevator, "noop"); + strcpy(chosen_elevator, CONFIG_DEFAULT_IOSCHED); } static int __init elevator_setup(char *str) @@ -304,15 +310,7 @@ void elv_requeue_request(request_queue_t *q, struct request *rq) rq->flags &= ~REQ_STARTED; - /* - * if this is the flush, requeue the original instead and drop the flush - */ - if (rq->flags & REQ_BAR_FLUSH) { - clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); - rq = rq->end_io_data; - } - - __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); + __elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE, 0); } static void elv_drain_elevator(request_queue_t *q) @@ -332,8 +330,19 @@ static void elv_drain_elevator(request_queue_t *q) void __elv_add_request(request_queue_t *q, struct request *rq, int where, int plug) { + struct list_head *pos; + unsigned ordseq; + + if (q->ordcolor) + rq->flags |= REQ_ORDERED_COLOR; + if (rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER)) { /* + * toggle ordered color + */ + q->ordcolor ^= 1; + + /* * barriers implicitly indicate back insertion */ if (where == ELEVATOR_INSERT_SORT) @@ -393,6 +402,30 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where, q->elevator->ops->elevator_add_req_fn(q, rq); break; + case ELEVATOR_INSERT_REQUEUE: + /* + * If ordered flush isn't in progress, we do front + * insertion; otherwise, requests should be requeued + * in ordseq order. + */ + rq->flags |= REQ_SOFTBARRIER; + + if (q->ordseq == 0) { + list_add(&rq->queuelist, &q->queue_head); + break; + } + + ordseq = blk_ordered_req_seq(rq); + + list_for_each(pos, &q->queue_head) { + struct request *pos_rq = list_entry_rq(pos); + if (ordseq <= blk_ordered_req_seq(pos_rq)) + break; + } + + list_add_tail(&rq->queuelist, pos); + break; + default: printk(KERN_ERR "%s: bad insertion point %d\n", __FUNCTION__, where); @@ -422,25 +455,16 @@ static inline struct request *__elv_next_request(request_queue_t *q) { struct request *rq; - if (unlikely(list_empty(&q->queue_head) && - !q->elevator->ops->elevator_dispatch_fn(q, 0))) - return NULL; - - rq = list_entry_rq(q->queue_head.next); - - /* - * if this is a barrier write and the device has to issue a - * flush sequence to support it, check how far we are - */ - if (blk_fs_request(rq) && blk_barrier_rq(rq)) { - BUG_ON(q->ordered == QUEUE_ORDERED_NONE); + while (1) { + while (!list_empty(&q->queue_head)) { + rq = list_entry_rq(q->queue_head.next); + if (blk_do_ordered(q, &rq)) + return rq; + } - if (q->ordered == QUEUE_ORDERED_FLUSH && - !blk_barrier_preflush(rq)) - rq = blk_start_pre_flush(q, rq); + if (!q->elevator->ops->elevator_dispatch_fn(q, 0)) + return NULL; } - - return rq; } struct request *elv_next_request(request_queue_t *q) @@ -498,7 +522,7 @@ struct request *elv_next_request(request_queue_t *q) blkdev_dequeue_request(rq); rq->flags |= REQ_QUIET; end_that_request_chunk(rq, 0, nr_bytes); - end_that_request_last(rq); + end_that_request_last(rq, 0); } else { printk(KERN_ERR "%s: bad return=%d\n", __FUNCTION__, ret); @@ -597,6 +621,20 @@ void elv_completed_request(request_queue_t *q, struct request *rq) if (blk_sorted_rq(rq) && e->ops->elevator_completed_req_fn) e->ops->elevator_completed_req_fn(q, rq); } + + /* + * Check if the queue is waiting for fs requests to be + * drained for flush sequence. + */ + if (unlikely(q->ordseq)) { + struct request *first_rq = list_entry_rq(q->queue_head.next); + if (q->in_flight == 0 && + blk_ordered_cur_seq(q) == QUEUE_ORDSEQ_DRAIN && + blk_ordered_req_seq(first_rq) > QUEUE_ORDSEQ_DRAIN) { + blk_ordered_complete_seq(q, QUEUE_ORDSEQ_DRAIN, 0); + q->request_fn(q); + } + } } int elv_register_queue(struct request_queue *q) diff --git a/block/genhd.c b/block/genhd.c index f04609d553b..db57546a709 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -38,34 +38,100 @@ static inline int major_to_index(int major) return major % MAX_PROBE_HASH; } -#ifdef CONFIG_PROC_FS -/* get block device names in somewhat random order */ -int get_blkdev_list(char *p, int used) +struct blkdev_info { + int index; + struct blk_major_name *bd; +}; + +/* + * iterate over a list of blkdev_info structures. allows + * the major_names array to be iterated over from outside this file + * must be called with the block_subsys_sem held + */ +void *get_next_blkdev(void *dev) +{ + struct blkdev_info *info; + + if (dev == NULL) { + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto out; + info->index=0; + info->bd = major_names[info->index]; + if (info->bd) + goto out; + } else { + info = dev; + } + + while (info->index < ARRAY_SIZE(major_names)) { + if (info->bd) + info->bd = info->bd->next; + if (info->bd) + goto out; + /* + * No devices on this chain, move to the next + */ + info->index++; + info->bd = (info->index < ARRAY_SIZE(major_names)) ? + major_names[info->index] : NULL; + if (info->bd) + goto out; + } + +out: + return info; +} + +void *acquire_blkdev_list(void) +{ + down(&block_subsys_sem); + return get_next_blkdev(NULL); +} + +void release_blkdev_list(void *dev) +{ + up(&block_subsys_sem); + kfree(dev); +} + + +/* + * Count the number of records in the blkdev_list. + * must be called with the block_subsys_sem held + */ +int count_blkdev_list(void) { struct blk_major_name *n; - int i, len; + int i, count; - len = snprintf(p, (PAGE_SIZE-used), "\nBlock devices:\n"); + count = 0; - down(&block_subsys_sem); for (i = 0; i < ARRAY_SIZE(major_names); i++) { - for (n = major_names[i]; n; n = n->next) { - /* - * If the curent string plus the 5 extra characters - * in the line would run us off the page, then we're done - */ - if ((len + used + strlen(n->name) + 5) >= PAGE_SIZE) - goto page_full; - len += sprintf(p+len, "%3d %s\n", - n->major, n->name); - } + for (n = major_names[i]; n; n = n->next) + count++; } -page_full: - up(&block_subsys_sem); - return len; + return count; } -#endif + +/* + * extract the major and name values from a blkdev_info struct + * passed in as a void to *dev. Must be called with + * block_subsys_sem held + */ +int get_blkdev_info(void *dev, int *major, char **name) +{ + struct blkdev_info *info = dev; + + if (info->bd == NULL) + return 1; + + *major = info->bd->major; + *name = info->bd->name; + return 0; +} + int register_blkdev(unsigned int major, const char *name) { @@ -358,7 +424,7 @@ static struct sysfs_ops disk_sysfs_ops = { static ssize_t disk_uevent_store(struct gendisk * disk, const char *buf, size_t count) { - kobject_hotplug(&disk->kobj, KOBJ_ADD); + kobject_uevent(&disk->kobj, KOBJ_ADD); return count; } static ssize_t disk_dev_read(struct gendisk * disk, char *page) @@ -455,14 +521,14 @@ static struct kobj_type ktype_block = { extern struct kobj_type ktype_part; -static int block_hotplug_filter(struct kset *kset, struct kobject *kobj) +static int block_uevent_filter(struct kset *kset, struct kobject *kobj) { struct kobj_type *ktype = get_ktype(kobj); return ((ktype == &ktype_block) || (ktype == &ktype_part)); } -static int block_hotplug(struct kset *kset, struct kobject *kobj, char **envp, +static int block_uevent(struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size) { struct kobj_type *ktype = get_ktype(kobj); @@ -474,40 +540,40 @@ static int block_hotplug(struct kset *kset, struct kobject *kobj, char **envp, if (ktype == &ktype_block) { disk = container_of(kobj, struct gendisk, kobj); - add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, - &length, "MINOR=%u", disk->first_minor); + add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "MINOR=%u", disk->first_minor); } else if (ktype == &ktype_part) { disk = container_of(kobj->parent, struct gendisk, kobj); part = container_of(kobj, struct hd_struct, kobj); - add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, - &length, "MINOR=%u", - disk->first_minor + part->partno); + add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "MINOR=%u", + disk->first_minor + part->partno); } else return 0; - add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &length, - "MAJOR=%u", disk->major); + add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, + "MAJOR=%u", disk->major); /* add physical device, backing this device */ physdev = disk->driverfs_dev; if (physdev) { char *path = kobject_get_path(&physdev->kobj, GFP_KERNEL); - add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, - &length, "PHYSDEVPATH=%s", path); + add_uevent_var(envp, num_envp, &i, buffer, buffer_size, + &length, "PHYSDEVPATH=%s", path); kfree(path); if (physdev->bus) - add_hotplug_env_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "PHYSDEVBUS=%s", - physdev->bus->name); + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "PHYSDEVBUS=%s", + physdev->bus->name); if (physdev->driver) - add_hotplug_env_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "PHYSDEVDRIVER=%s", - physdev->driver->name); + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "PHYSDEVDRIVER=%s", + physdev->driver->name); } /* terminate, set to next free slot, shrink available space */ @@ -520,13 +586,13 @@ static int block_hotplug(struct kset *kset, struct kobject *kobj, char **envp, return 0; } -static struct kset_hotplug_ops block_hotplug_ops = { - .filter = block_hotplug_filter, - .hotplug = block_hotplug, +static struct kset_uevent_ops block_uevent_ops = { + .filter = block_uevent_filter, + .uevent = block_uevent, }; /* declare block_subsys. */ -static decl_subsys(block, &ktype_block, &block_hotplug_ops); +static decl_subsys(block, &ktype_block, &block_uevent_ops); /* diff --git a/block/ioctl.c b/block/ioctl.c index 6e278474f9a..e1109491c23 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -1,6 +1,7 @@ -#include <linux/sched.h> /* for capable() */ +#include <linux/capability.h> #include <linux/blkdev.h> #include <linux/blkpg.h> +#include <linux/hdreg.h> #include <linux/backing-dev.h> #include <linux/buffer_head.h> #include <linux/smp_lock.h> @@ -245,6 +246,27 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, set_device_ro(bdev, n); unlock_kernel(); return 0; + case HDIO_GETGEO: { + struct hd_geometry geo; + + if (!arg) + return -EINVAL; + if (!disk->fops->getgeo) + return -ENOTTY; + + /* + * We need to set the startsect first, the driver may + * want to override it. + */ + geo.start = get_start_sect(bdev); + ret = disk->fops->getgeo(bdev, &geo); + if (ret) + return ret; + if (copy_to_user((struct hd_geometry __user *)arg, &geo, + sizeof(geo))) + return -EFAULT; + return 0; + } } lock_kernel(); diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index 99c9ca6d599..8e27d0ab0d7 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -26,7 +26,8 @@ #include <linux/slab.h> #include <linux/swap.h> #include <linux/writeback.h> -#include <linux/blkdev.h> +#include <linux/interrupt.h> +#include <linux/cpu.h> /* * for max sense size @@ -36,6 +37,8 @@ static void blk_unplug_work(void *data); static void blk_unplug_timeout(unsigned long data); static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io); +static void init_request_from_bio(struct request *req, struct bio *bio); +static int __make_request(request_queue_t *q, struct bio *bio); /* * For the allocated request tables @@ -60,13 +63,15 @@ static wait_queue_head_t congestion_wqh[2] = { /* * Controlling structure to kblockd */ -static struct workqueue_struct *kblockd_workqueue; +static struct workqueue_struct *kblockd_workqueue; unsigned long blk_max_low_pfn, blk_max_pfn; EXPORT_SYMBOL(blk_max_low_pfn); EXPORT_SYMBOL(blk_max_pfn); +static DEFINE_PER_CPU(struct list_head, blk_cpu_done); + /* Amount of time in which a process may batch requests */ #define BLK_BATCH_TIME (HZ/50UL) @@ -205,6 +210,13 @@ void blk_queue_merge_bvec(request_queue_t *q, merge_bvec_fn *mbfn) EXPORT_SYMBOL(blk_queue_merge_bvec); +void blk_queue_softirq_done(request_queue_t *q, softirq_done_fn *fn) +{ + q->softirq_done_fn = fn; +} + +EXPORT_SYMBOL(blk_queue_softirq_done); + /** * blk_queue_make_request - define an alternate make_request function for a device * @q: the request queue for the device to be affected @@ -239,7 +251,7 @@ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn) q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; q->backing_dev_info.state = 0; q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY; - blk_queue_max_sectors(q, MAX_SECTORS); + blk_queue_max_sectors(q, SAFE_MAX_SECTORS); blk_queue_hardsect_size(q, 512); blk_queue_dma_alignment(q, 511); blk_queue_congestion_threshold(q); @@ -268,6 +280,7 @@ EXPORT_SYMBOL(blk_queue_make_request); static inline void rq_init(request_queue_t *q, struct request *rq) { INIT_LIST_HEAD(&rq->queuelist); + INIT_LIST_HEAD(&rq->donelist); rq->errors = 0; rq->rq_status = RQ_ACTIVE; @@ -284,12 +297,13 @@ static inline void rq_init(request_queue_t *q, struct request *rq) rq->sense = NULL; rq->end_io = NULL; rq->end_io_data = NULL; + rq->completion_data = NULL; } /** * blk_queue_ordered - does this queue support ordered writes - * @q: the request queue - * @flag: see below + * @q: the request queue + * @ordered: one of QUEUE_ORDERED_* * * Description: * For journalled file systems, doing ordered writes on a commit @@ -298,28 +312,30 @@ static inline void rq_init(request_queue_t *q, struct request *rq) * feature should call this function and indicate so. * **/ -void blk_queue_ordered(request_queue_t *q, int flag) -{ - switch (flag) { - case QUEUE_ORDERED_NONE: - if (q->flush_rq) - kmem_cache_free(request_cachep, q->flush_rq); - q->flush_rq = NULL; - q->ordered = flag; - break; - case QUEUE_ORDERED_TAG: - q->ordered = flag; - break; - case QUEUE_ORDERED_FLUSH: - q->ordered = flag; - if (!q->flush_rq) - q->flush_rq = kmem_cache_alloc(request_cachep, - GFP_KERNEL); - break; - default: - printk("blk_queue_ordered: bad value %d\n", flag); - break; +int blk_queue_ordered(request_queue_t *q, unsigned ordered, + prepare_flush_fn *prepare_flush_fn) +{ + if (ordered & (QUEUE_ORDERED_PREFLUSH | QUEUE_ORDERED_POSTFLUSH) && + prepare_flush_fn == NULL) { + printk(KERN_ERR "blk_queue_ordered: prepare_flush_fn required\n"); + return -EINVAL; } + + if (ordered != QUEUE_ORDERED_NONE && + ordered != QUEUE_ORDERED_DRAIN && + ordered != QUEUE_ORDERED_DRAIN_FLUSH && + ordered != QUEUE_ORDERED_DRAIN_FUA && + ordered != QUEUE_ORDERED_TAG && + ordered != QUEUE_ORDERED_TAG_FLUSH && + ordered != QUEUE_ORDERED_TAG_FUA) { + printk(KERN_ERR "blk_queue_ordered: bad value %d\n", ordered); + return -EINVAL; + } + + q->next_ordered = ordered; + q->prepare_flush_fn = prepare_flush_fn; + + return 0; } EXPORT_SYMBOL(blk_queue_ordered); @@ -344,167 +360,265 @@ EXPORT_SYMBOL(blk_queue_issue_flush_fn); /* * Cache flushing for ordered writes handling */ -static void blk_pre_flush_end_io(struct request *flush_rq) +inline unsigned blk_ordered_cur_seq(request_queue_t *q) { - struct request *rq = flush_rq->end_io_data; - request_queue_t *q = rq->q; - - elv_completed_request(q, flush_rq); - - rq->flags |= REQ_BAR_PREFLUSH; - - if (!flush_rq->errors) - elv_requeue_request(q, rq); - else { - q->end_flush_fn(q, flush_rq); - clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); - q->request_fn(q); - } + if (!q->ordseq) + return 0; + return 1 << ffz(q->ordseq); } -static void blk_post_flush_end_io(struct request *flush_rq) +unsigned blk_ordered_req_seq(struct request *rq) { - struct request *rq = flush_rq->end_io_data; request_queue_t *q = rq->q; - elv_completed_request(q, flush_rq); + BUG_ON(q->ordseq == 0); - rq->flags |= REQ_BAR_POSTFLUSH; + if (rq == &q->pre_flush_rq) + return QUEUE_ORDSEQ_PREFLUSH; + if (rq == &q->bar_rq) + return QUEUE_ORDSEQ_BAR; + if (rq == &q->post_flush_rq) + return QUEUE_ORDSEQ_POSTFLUSH; - q->end_flush_fn(q, flush_rq); - clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); - q->request_fn(q); + if ((rq->flags & REQ_ORDERED_COLOR) == + (q->orig_bar_rq->flags & REQ_ORDERED_COLOR)) + return QUEUE_ORDSEQ_DRAIN; + else + return QUEUE_ORDSEQ_DONE; } -struct request *blk_start_pre_flush(request_queue_t *q, struct request *rq) +void blk_ordered_complete_seq(request_queue_t *q, unsigned seq, int error) { - struct request *flush_rq = q->flush_rq; - - BUG_ON(!blk_barrier_rq(rq)); + struct request *rq; + int uptodate; - if (test_and_set_bit(QUEUE_FLAG_FLUSH, &q->queue_flags)) - return NULL; + if (error && !q->orderr) + q->orderr = error; - rq_init(q, flush_rq); - flush_rq->elevator_private = NULL; - flush_rq->flags = REQ_BAR_FLUSH; - flush_rq->rq_disk = rq->rq_disk; - flush_rq->rl = NULL; + BUG_ON(q->ordseq & seq); + q->ordseq |= seq; - /* - * prepare_flush returns 0 if no flush is needed, just mark both - * pre and post flush as done in that case - */ - if (!q->prepare_flush_fn(q, flush_rq)) { - rq->flags |= REQ_BAR_PREFLUSH | REQ_BAR_POSTFLUSH; - clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); - return rq; - } + if (blk_ordered_cur_seq(q) != QUEUE_ORDSEQ_DONE) + return; /* - * some drivers dequeue requests right away, some only after io - * completion. make sure the request is dequeued. + * Okay, sequence complete. */ - if (!list_empty(&rq->queuelist)) - blkdev_dequeue_request(rq); + rq = q->orig_bar_rq; + uptodate = q->orderr ? q->orderr : 1; - flush_rq->end_io_data = rq; - flush_rq->end_io = blk_pre_flush_end_io; + q->ordseq = 0; - __elv_add_request(q, flush_rq, ELEVATOR_INSERT_FRONT, 0); - return flush_rq; + end_that_request_first(rq, uptodate, rq->hard_nr_sectors); + end_that_request_last(rq, uptodate); } -static void blk_start_post_flush(request_queue_t *q, struct request *rq) +static void pre_flush_end_io(struct request *rq, int error) { - struct request *flush_rq = q->flush_rq; + elv_completed_request(rq->q, rq); + blk_ordered_complete_seq(rq->q, QUEUE_ORDSEQ_PREFLUSH, error); +} - BUG_ON(!blk_barrier_rq(rq)); +static void bar_end_io(struct request *rq, int error) +{ + elv_completed_request(rq->q, rq); + blk_ordered_complete_seq(rq->q, QUEUE_ORDSEQ_BAR, error); +} - rq_init(q, flush_rq); - flush_rq->elevator_private = NULL; - flush_rq->flags = REQ_BAR_FLUSH; - flush_rq->rq_disk = rq->rq_disk; - flush_rq->rl = NULL; +static void post_flush_end_io(struct request *rq, int error) +{ + elv_completed_request(rq->q, rq); + blk_ordered_complete_seq(rq->q, QUEUE_ORDSEQ_POSTFLUSH, error); +} - if (q->prepare_flush_fn(q, flush_rq)) { - flush_rq->end_io_data = rq; - flush_rq->end_io = blk_post_flush_end_io; +static void queue_flush(request_queue_t *q, unsigned which) +{ + struct request *rq; + rq_end_io_fn *end_io; - __elv_add_request(q, flush_rq, ELEVATOR_INSERT_FRONT, 0); - q->request_fn(q); + if (which == QUEUE_ORDERED_PREFLUSH) { + rq = &q->pre_flush_rq; + end_io = pre_flush_end_io; + } else { + rq = &q->post_flush_rq; + end_io = post_flush_end_io; } + + rq_init(q, rq); + rq->flags = REQ_HARDBARRIER; + rq->elevator_private = NULL; + rq->rq_disk = q->bar_rq.rq_disk; + rq->rl = NULL; + rq->end_io = end_io; + q->prepare_flush_fn(q, rq); + + __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); } -static inline int blk_check_end_barrier(request_queue_t *q, struct request *rq, - int sectors) +static inline struct request *start_ordered(request_queue_t *q, + struct request *rq) { - if (sectors > rq->nr_sectors) - sectors = rq->nr_sectors; + q->bi_size = 0; + q->orderr = 0; + q->ordered = q->next_ordered; + q->ordseq |= QUEUE_ORDSEQ_STARTED; + + /* + * Prep proxy barrier request. + */ + blkdev_dequeue_request(rq); + q->orig_bar_rq = rq; + rq = &q->bar_rq; + rq_init(q, rq); + rq->flags = bio_data_dir(q->orig_bar_rq->bio); + rq->flags |= q->ordered & QUEUE_ORDERED_FUA ? REQ_FUA : 0; + rq->elevator_private = NULL; + rq->rl = NULL; + init_request_from_bio(rq, q->orig_bar_rq->bio); + rq->end_io = bar_end_io; + + /* + * Queue ordered sequence. As we stack them at the head, we + * need to queue in reverse order. Note that we rely on that + * no fs request uses ELEVATOR_INSERT_FRONT and thus no fs + * request gets inbetween ordered sequence. + */ + if (q->ordered & QUEUE_ORDERED_POSTFLUSH) + queue_flush(q, QUEUE_ORDERED_POSTFLUSH); + else + q->ordseq |= QUEUE_ORDSEQ_POSTFLUSH; + + __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0); - rq->nr_sectors -= sectors; - return rq->nr_sectors; + if (q->ordered & QUEUE_ORDERED_PREFLUSH) { + queue_flush(q, QUEUE_ORDERED_PREFLUSH); + rq = &q->pre_flush_rq; + } else + q->ordseq |= QUEUE_ORDSEQ_PREFLUSH; + + if ((q->ordered & QUEUE_ORDERED_TAG) || q->in_flight == 0) + q->ordseq |= QUEUE_ORDSEQ_DRAIN; + else + rq = NULL; + + return rq; } -static int __blk_complete_barrier_rq(request_queue_t *q, struct request *rq, - int sectors, int queue_locked) +int blk_do_ordered(request_queue_t *q, struct request **rqp) { - if (q->ordered != QUEUE_ORDERED_FLUSH) - return 0; - if (!blk_fs_request(rq) || !blk_barrier_rq(rq)) - return 0; - if (blk_barrier_postflush(rq)) - return 0; + struct request *rq = *rqp, *allowed_rq; + int is_barrier = blk_fs_request(rq) && blk_barrier_rq(rq); - if (!blk_check_end_barrier(q, rq, sectors)) { - unsigned long flags = 0; + if (!q->ordseq) { + if (!is_barrier) + return 1; - if (!queue_locked) - spin_lock_irqsave(q->queue_lock, flags); + if (q->next_ordered != QUEUE_ORDERED_NONE) { + *rqp = start_ordered(q, rq); + return 1; + } else { + /* + * This can happen when the queue switches to + * ORDERED_NONE while this request is on it. + */ + blkdev_dequeue_request(rq); + end_that_request_first(rq, -EOPNOTSUPP, + rq->hard_nr_sectors); + end_that_request_last(rq, -EOPNOTSUPP); + *rqp = NULL; + return 0; + } + } - blk_start_post_flush(q, rq); + if (q->ordered & QUEUE_ORDERED_TAG) { + if (is_barrier && rq != &q->bar_rq) + *rqp = NULL; + return 1; + } - if (!queue_locked) - spin_unlock_irqrestore(q->queue_lock, flags); + switch (blk_ordered_cur_seq(q)) { + case QUEUE_ORDSEQ_PREFLUSH: + allowed_rq = &q->pre_flush_rq; + break; + case QUEUE_ORDSEQ_BAR: + allowed_rq = &q->bar_rq; + break; + case QUEUE_ORDSEQ_POSTFLUSH: + allowed_rq = &q->post_flush_rq; + break; + default: + allowed_rq = NULL; + break; } + if (rq != allowed_rq && + (blk_fs_request(rq) || rq == &q->pre_flush_rq || + rq == &q->post_flush_rq)) + *rqp = NULL; + return 1; } -/** - * blk_complete_barrier_rq - complete possible barrier request - * @q: the request queue for the device - * @rq: the request - * @sectors: number of sectors to complete - * - * Description: - * Used in driver end_io handling to determine whether to postpone - * completion of a barrier request until a post flush has been done. This - * is the unlocked variant, used if the caller doesn't already hold the - * queue lock. - **/ -int blk_complete_barrier_rq(request_queue_t *q, struct request *rq, int sectors) +static int flush_dry_bio_endio(struct bio *bio, unsigned int bytes, int error) { - return __blk_complete_barrier_rq(q, rq, sectors, 0); + request_queue_t *q = bio->bi_private; + struct bio_vec *bvec; + int i; + + /* + * This is dry run, restore bio_sector and size. We'll finish + * this request again with the original bi_end_io after an + * error occurs or post flush is complete. + */ + q->bi_size += bytes; + + if (bio->bi_size) + return 1; + + /* Rewind bvec's */ + bio->bi_idx = 0; + bio_for_each_segment(bvec, bio, i) { + bvec->bv_len += bvec->bv_offset; + bvec->bv_offset = 0; + } + + /* Reset bio */ + set_bit(BIO_UPTODATE, &bio->bi_flags); + bio->bi_size = q->bi_size; + bio->bi_sector -= (q->bi_size >> 9); + q->bi_size = 0; + + return 0; } -EXPORT_SYMBOL(blk_complete_barrier_rq); -/** - * blk_complete_barrier_rq_locked - complete possible barrier request - * @q: the request queue for the device - * @rq: the request - * @sectors: number of sectors to complete - * - * Description: - * See blk_complete_barrier_rq(). This variant must be used if the caller - * holds the queue lock. - **/ -int blk_complete_barrier_rq_locked(request_queue_t *q, struct request *rq, - int sectors) +static inline int ordered_bio_endio(struct request *rq, struct bio *bio, + unsigned int nbytes, int error) { - return __blk_complete_barrier_rq(q, rq, sectors, 1); + request_queue_t *q = rq->q; + bio_end_io_t *endio; + void *private; + + if (&q->bar_rq != rq) + return 0; + + /* + * Okay, this is the barrier request in progress, dry finish it. + */ + if (error && !q->orderr) + q->orderr = error; + + endio = bio->bi_end_io; + private = bio->bi_private; + bio->bi_end_io = flush_dry_bio_endio; + bio->bi_private = q; + + bio_endio(bio, nbytes, error); + + bio->bi_end_io = endio; + bio->bi_private = private; + + return 1; } -EXPORT_SYMBOL(blk_complete_barrier_rq_locked); /** * blk_queue_bounce_limit - set bounce buffer limit for queue @@ -555,7 +669,12 @@ void blk_queue_max_sectors(request_queue_t *q, unsigned short max_sectors) printk("%s: set to minimum %d\n", __FUNCTION__, max_sectors); } - q->max_sectors = q->max_hw_sectors = max_sectors; + if (BLK_DEF_MAX_SECTORS > max_sectors) + q->max_hw_sectors = q->max_sectors = max_sectors; + else { + q->max_sectors = BLK_DEF_MAX_SECTORS; + q->max_hw_sectors = max_sectors; + } } EXPORT_SYMBOL(blk_queue_max_sectors); @@ -657,8 +776,8 @@ EXPORT_SYMBOL(blk_queue_hardsect_size); void blk_queue_stack_limits(request_queue_t *t, request_queue_t *b) { /* zero is "infinity" */ - t->max_sectors = t->max_hw_sectors = - min_not_zero(t->max_sectors,b->max_sectors); + t->max_sectors = min_not_zero(t->max_sectors,b->max_sectors); + t->max_hw_sectors = min_not_zero(t->max_hw_sectors,b->max_hw_sectors); t->max_phys_segments = min(t->max_phys_segments,b->max_phys_segments); t->max_hw_segments = min(t->max_hw_segments,b->max_hw_segments); @@ -1034,12 +1153,13 @@ void blk_queue_invalidate_tags(request_queue_t *q) EXPORT_SYMBOL(blk_queue_invalidate_tags); -static char *rq_flags[] = { +static const char * const rq_flags[] = { "REQ_RW", "REQ_FAILFAST", "REQ_SORTED", "REQ_SOFTBARRIER", "REQ_HARDBARRIER", + "REQ_FUA", "REQ_CMD", "REQ_NOMERGE", "REQ_STARTED", @@ -1059,6 +1179,7 @@ static char *rq_flags[] = { "REQ_PM_SUSPEND", "REQ_PM_RESUME", "REQ_PM_SHUTDOWN", + "REQ_ORDERED_COLOR", }; void blk_dump_rq_flags(struct request *rq, char *msg) @@ -1293,9 +1414,15 @@ static inline int ll_new_hw_segment(request_queue_t *q, static int ll_back_merge_fn(request_queue_t *q, struct request *req, struct bio *bio) { + unsigned short max_sectors; int len; - if (req->nr_sectors + bio_sectors(bio) > q->max_sectors) { + if (unlikely(blk_pc_request(req))) + max_sectors = q->max_hw_sectors; + else + max_sectors = q->max_sectors; + + if (req->nr_sectors + bio_sectors(bio) > max_sectors) { req->flags |= REQ_NOMERGE; if (req == q->last_merge) q->last_merge = NULL; @@ -1325,9 +1452,16 @@ static int ll_back_merge_fn(request_queue_t *q, struct request *req, static int ll_front_merge_fn(request_queue_t *q, struct request *req, struct bio *bio) { + unsigned short max_sectors; int len; - if (req->nr_sectors + bio_sectors(bio) > q->max_sectors) { + if (unlikely(blk_pc_request(req))) + max_sectors = q->max_hw_sectors; + else + max_sectors = q->max_sectors; + + + if (req->nr_sectors + bio_sectors(bio) > max_sectors) { req->flags |= REQ_NOMERGE; if (req == q->last_merge) q->last_merge = NULL; @@ -1623,8 +1757,6 @@ void blk_cleanup_queue(request_queue_t * q) if (q->queue_tags) __blk_queue_free_tags(q); - blk_queue_ordered(q, QUEUE_ORDERED_NONE); - kmem_cache_free(requestq_cachep, q); } @@ -1649,8 +1781,6 @@ static int blk_init_free_list(request_queue_t *q) return 0; } -static int __make_request(request_queue_t *, struct bio *); - request_queue_t *blk_alloc_queue(gfp_t gfp_mask) { return blk_alloc_queue_node(gfp_mask, -1); @@ -1890,40 +2020,40 @@ static struct request *get_request(request_queue_t *q, int rw, struct bio *bio, { struct request *rq = NULL; struct request_list *rl = &q->rq; - struct io_context *ioc = current_io_context(GFP_ATOMIC); - int priv; - - if (rl->count[rw]+1 >= q->nr_requests) { - /* - * The queue will fill after this allocation, so set it as - * full, and mark this process as "batching". This process - * will be allowed to complete a batch of requests, others - * will be blocked. - */ - if (!blk_queue_full(q, rw)) { - ioc_set_batching(q, ioc); - blk_set_queue_full(q, rw); - } - } + struct io_context *ioc = NULL; + int may_queue, priv; - switch (elv_may_queue(q, rw, bio)) { - case ELV_MQUEUE_NO: - goto rq_starved; - case ELV_MQUEUE_MAY: - break; - case ELV_MQUEUE_MUST: - goto get_rq; - } + may_queue = elv_may_queue(q, rw, bio); + if (may_queue == ELV_MQUEUE_NO) + goto rq_starved; - if (blk_queue_full(q, rw) && !ioc_batching(q, ioc)) { - /* - * The queue is full and the allocating process is not a - * "batcher", and not exempted by the IO scheduler - */ - goto out; + if (rl->count[rw]+1 >= queue_congestion_on_threshold(q)) { + if (rl->count[rw]+1 >= q->nr_requests) { + ioc = current_io_context(GFP_ATOMIC); + /* + * The queue will fill after this allocation, so set + * it as full, and mark this process as "batching". + * This process will be allowed to complete a batch of + * requests, others will be blocked. + */ + if (!blk_queue_full(q, rw)) { + ioc_set_batching(q, ioc); + blk_set_queue_full(q, rw); + } else { + if (may_queue != ELV_MQUEUE_MUST + && !ioc_batching(q, ioc)) { + /* + * The queue is full and the allocating + * process is not a "batcher", and not + * exempted by the IO scheduler + */ + goto out; + } + } + } + set_queue_congested(q, rw); } -get_rq: /* * Only allow batching queuers to allocate up to 50% over the defined * limit of requests, otherwise we could have thousands of requests @@ -1934,8 +2064,6 @@ get_rq: rl->count[rw]++; rl->starved[rw] = 0; - if (rl->count[rw] >= queue_congestion_on_threshold(q)) - set_queue_congested(q, rw); priv = !test_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags); if (priv) @@ -1944,7 +2072,7 @@ get_rq: spin_unlock_irq(q->queue_lock); rq = blk_alloc_request(q, rw, bio, priv, gfp_mask); - if (!rq) { + if (unlikely(!rq)) { /* * Allocation failed presumably due to memory. Undo anything * we might have messed up. @@ -1969,6 +2097,12 @@ rq_starved: goto out; } + /* + * ioc may be NULL here, and ioc_batching will be false. That's + * OK, if the queue is under the request limit then requests need + * not count toward the nr_batch_requests limit. There will always + * be some limit enforced by BLK_BATCH_TIME. + */ if (ioc_batching(q, ioc)) ioc->nr_batch_requests--; @@ -2144,7 +2278,7 @@ int blk_rq_map_user(request_queue_t *q, struct request *rq, void __user *ubuf, struct bio *bio; int reading; - if (len > (q->max_sectors << 9)) + if (len > (q->max_hw_sectors << 9)) return -EINVAL; if (!len || !ubuf) return -EINVAL; @@ -2259,7 +2393,7 @@ int blk_rq_map_kern(request_queue_t *q, struct request *rq, void *kbuf, { struct bio *bio; - if (len > (q->max_sectors << 9)) + if (len > (q->max_hw_sectors << 9)) return -EINVAL; if (!len || !kbuf) return -EINVAL; @@ -2295,7 +2429,7 @@ EXPORT_SYMBOL(blk_rq_map_kern); */ void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk, struct request *rq, int at_head, - void (*done)(struct request *)) + rq_end_io_fn *done) { int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK; @@ -2306,6 +2440,8 @@ void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk, generic_unplug_device(q); } +EXPORT_SYMBOL_GPL(blk_execute_rq_nowait); + /** * blk_execute_rq - insert a request into queue for execution * @q: queue to insert the request in @@ -2444,7 +2580,7 @@ void disk_round_stats(struct gendisk *disk) /* * queue lock must be held */ -static void __blk_put_request(request_queue_t *q, struct request *req) +void __blk_put_request(request_queue_t *q, struct request *req) { struct request_list *rl = req->rl; @@ -2473,6 +2609,8 @@ static void __blk_put_request(request_queue_t *q, struct request *req) } } +EXPORT_SYMBOL_GPL(__blk_put_request); + void blk_put_request(struct request *req) { unsigned long flags; @@ -2495,7 +2633,7 @@ EXPORT_SYMBOL(blk_put_request); * blk_end_sync_rq - executes a completion event on a request * @rq: request to complete */ -void blk_end_sync_rq(struct request *rq) +void blk_end_sync_rq(struct request *rq, int error) { struct completion *waiting = rq->waiting; @@ -2609,29 +2747,35 @@ static inline int attempt_front_merge(request_queue_t *q, struct request *rq) return 0; } -/** - * blk_attempt_remerge - attempt to remerge active head with next request - * @q: The &request_queue_t belonging to the device - * @rq: The head request (usually) - * - * Description: - * For head-active devices, the queue can easily be unplugged so quickly - * that proper merging is not done on the front request. This may hurt - * performance greatly for some devices. The block layer cannot safely - * do merging on that first request for these queues, but the driver can - * call this function and make it happen any way. Only the driver knows - * when it is safe to do so. - **/ -void blk_attempt_remerge(request_queue_t *q, struct request *rq) +static void init_request_from_bio(struct request *req, struct bio *bio) { - unsigned long flags; + req->flags |= REQ_CMD; - spin_lock_irqsave(q->queue_lock, flags); - attempt_back_merge(q, rq); - spin_unlock_irqrestore(q->queue_lock, flags); -} + /* + * inherit FAILFAST from bio (for read-ahead, and explicit FAILFAST) + */ + if (bio_rw_ahead(bio) || bio_failfast(bio)) + req->flags |= REQ_FAILFAST; -EXPORT_SYMBOL(blk_attempt_remerge); + /* + * REQ_BARRIER implies no merging, but lets make it explicit + */ + if (unlikely(bio_barrier(bio))) + req->flags |= (REQ_HARDBARRIER | REQ_NOMERGE); + + req->errors = 0; + req->hard_sector = req->sector = bio->bi_sector; + req->hard_nr_sectors = req->nr_sectors = bio_sectors(bio); + req->current_nr_sectors = req->hard_cur_sectors = bio_cur_sectors(bio); + req->nr_phys_segments = bio_phys_segments(req->q, bio); + req->nr_hw_segments = bio_hw_segments(req->q, bio); + req->buffer = bio_data(bio); /* see ->buffer comment above */ + req->waiting = NULL; + req->bio = req->biotail = bio; + req->ioprio = bio_prio(bio); + req->rq_disk = bio->bi_bdev->bd_disk; + req->start_time = jiffies; +} static int __make_request(request_queue_t *q, struct bio *bio) { @@ -2658,7 +2802,7 @@ static int __make_request(request_queue_t *q, struct bio *bio) spin_lock_prefetch(q->queue_lock); barrier = bio_barrier(bio); - if (unlikely(barrier) && (q->ordered == QUEUE_ORDERED_NONE)) { + if (unlikely(barrier) && (q->next_ordered == QUEUE_ORDERED_NONE)) { err = -EOPNOTSUPP; goto end_io; } @@ -2728,33 +2872,7 @@ get_rq: * We don't worry about that case for efficiency. It won't happen * often, and the elevators are able to handle it. */ - - req->flags |= REQ_CMD; - - /* - * inherit FAILFAST from bio (for read-ahead, and explicit FAILFAST) - */ - if (bio_rw_ahead(bio) || bio_failfast(bio)) - req->flags |= REQ_FAILFAST; - - /* - * REQ_BARRIER implies no merging, but lets make it explicit - */ - if (unlikely(barrier)) - req->flags |= (REQ_HARDBARRIER | REQ_NOMERGE); - - req->errors = 0; - req->hard_sector = req->sector = sector; - req->hard_nr_sectors = req->nr_sectors = nr_sectors; - req->current_nr_sectors = req->hard_cur_sectors = cur_nr_sectors; - req->nr_phys_segments = bio_phys_segments(q, bio); - req->nr_hw_segments = bio_hw_segments(q, bio); - req->buffer = bio_data(bio); /* see ->buffer comment above */ - req->waiting = NULL; - req->bio = req->biotail = bio; - req->ioprio = prio; - req->rq_disk = bio->bi_bdev->bd_disk; - req->start_time = jiffies; + init_request_from_bio(req, bio); spin_lock_irq(q->queue_lock); if (elv_queue_empty(q)) @@ -3045,7 +3163,8 @@ static int __end_that_request_first(struct request *req, int uptodate, if (nr_bytes >= bio->bi_size) { req->bio = bio->bi_next; nbytes = bio->bi_size; - bio_endio(bio, nbytes, error); + if (!ordered_bio_endio(req, bio, nbytes, error)) + bio_endio(bio, nbytes, error); next_idx = 0; bio_nbytes = 0; } else { @@ -3100,7 +3219,8 @@ static int __end_that_request_first(struct request *req, int uptodate, * if the request wasn't completed, update state */ if (bio_nbytes) { - bio_endio(bio, bio_nbytes, error); + if (!ordered_bio_endio(req, bio, bio_nbytes, error)) + bio_endio(bio, bio_nbytes, error); bio->bi_idx += next_idx; bio_iovec(bio)->bv_offset += nr_bytes; bio_iovec(bio)->bv_len -= nr_bytes; @@ -3155,11 +3275,100 @@ int end_that_request_chunk(struct request *req, int uptodate, int nr_bytes) EXPORT_SYMBOL(end_that_request_chunk); /* + * splice the completion data to a local structure and hand off to + * process_completion_queue() to complete the requests + */ +static void blk_done_softirq(struct softirq_action *h) +{ + struct list_head *cpu_list; + LIST_HEAD(local_list); + + local_irq_disable(); + cpu_list = &__get_cpu_var(blk_cpu_done); + list_splice_init(cpu_list, &local_list); + local_irq_enable(); + + while (!list_empty(&local_list)) { + struct request *rq = list_entry(local_list.next, struct request, donelist); + + list_del_init(&rq->donelist); + rq->q->softirq_done_fn(rq); + } +} + +#ifdef CONFIG_HOTPLUG_CPU + +static int blk_cpu_notify(struct notifier_block *self, unsigned long action, + void *hcpu) +{ + /* + * If a CPU goes away, splice its entries to the current CPU + * and trigger a run of the softirq + */ + if (action == CPU_DEAD) { + int cpu = (unsigned long) hcpu; + + local_irq_disable(); + list_splice_init(&per_cpu(blk_cpu_done, cpu), + &__get_cpu_var(blk_cpu_done)); + raise_softirq_irqoff(BLOCK_SOFTIRQ); + local_irq_enable(); + } + + return NOTIFY_OK; +} + + +static struct notifier_block __devinitdata blk_cpu_notifier = { + .notifier_call = blk_cpu_notify, +}; + +#endif /* CONFIG_HOTPLUG_CPU */ + +/** + * blk_complete_request - end I/O on a request + * @req: the request being processed + * + * Description: + * Ends all I/O on a request. It does not handle partial completions, + * unless the driver actually implements this in its completionc callback + * through requeueing. Theh actual completion happens out-of-order, + * through a softirq handler. The user must have registered a completion + * callback through blk_queue_softirq_done(). + **/ + +void blk_complete_request(struct request *req) +{ + struct list_head *cpu_list; + unsigned long flags; + + BUG_ON(!req->q->softirq_done_fn); + + local_irq_save(flags); + + cpu_list = &__get_cpu_var(blk_cpu_done); + list_add_tail(&req->donelist, cpu_list); + raise_softirq_irqoff(BLOCK_SOFTIRQ); + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(blk_complete_request); + +/* * queue lock must be held */ -void end_that_request_last(struct request *req) +void end_that_request_last(struct request *req, int uptodate) { struct gendisk *disk = req->rq_disk; + int error; + + /* + * extend uptodate bool to allow < 0 value to be direct io error + */ + error = 0; + if (end_io_error(uptodate)) + error = !uptodate ? -EIO : uptodate; if (unlikely(laptop_mode) && blk_fs_request(req)) laptop_io_completion(); @@ -3174,7 +3383,7 @@ void end_that_request_last(struct request *req) disk->in_flight--; } if (req->end_io) - req->end_io(req); + req->end_io(req, error); else __blk_put_request(req->q, req); } @@ -3186,7 +3395,7 @@ void end_request(struct request *req, int uptodate) if (!end_that_request_first(req, uptodate, req->hard_cur_sectors)) { add_disk_randomness(req->rq_disk); blkdev_dequeue_request(req); - end_that_request_last(req); + end_that_request_last(req, uptodate); } } @@ -3224,6 +3433,8 @@ EXPORT_SYMBOL(kblockd_flush); int __init blk_dev_init(void) { + int i; + kblockd_workqueue = create_workqueue("kblockd"); if (!kblockd_workqueue) panic("Failed to create kblockd\n"); @@ -3237,6 +3448,14 @@ int __init blk_dev_init(void) iocontext_cachep = kmem_cache_create("blkdev_ioc", sizeof(struct io_context), 0, SLAB_PANIC, NULL, NULL); + for (i = 0; i < NR_CPUS; i++) + INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i)); + + open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL); +#ifdef CONFIG_HOTPLUG_CPU + register_cpu_notifier(&blk_cpu_notifier); +#endif + blk_max_low_pfn = max_low_pfn; blk_max_pfn = max_pfn; diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 382dea7b224..cc72210687e 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -21,6 +21,7 @@ #include <linux/string.h> #include <linux/module.h> #include <linux/blkdev.h> +#include <linux/capability.h> #include <linux/completion.h> #include <linux/cdrom.h> #include <linux/slab.h> @@ -46,7 +47,7 @@ EXPORT_SYMBOL(scsi_command_size); static int sg_get_version(int __user *p) { - static int sg_version_num = 30527; + static const int sg_version_num = 30527; return put_user(sg_version_num, p); } @@ -190,16 +191,21 @@ static int verify_command(struct file *file, unsigned char *cmd) safe_for_write(GPCMD_SET_STREAMING), }; unsigned char type = cmd_type[cmd[0]]; + int has_write_perm = 0; /* Anybody who can open the device can do a read-safe command */ if (type & CMD_READ_SAFE) return 0; + /* + * file can be NULL from ioctl_by_bdev()... + */ + if (file) + has_write_perm = file->f_mode & FMODE_WRITE; + /* Write-safe commands just require a writable open.. */ - if (type & CMD_WRITE_SAFE) { - if (file->f_mode & FMODE_WRITE) - return 0; - } + if ((type & CMD_WRITE_SAFE) && has_write_perm) + return 0; /* And root can do any command.. */ if (capable(CAP_SYS_RAWIO)) @@ -233,7 +239,7 @@ static int sg_io(struct file *file, request_queue_t *q, if (verify_command(file, cmd)) return -EPERM; - if (hdr->dxfer_len > (q->max_sectors << 9)) + if (hdr->dxfer_len > (q->max_hw_sectors << 9)) return -EIO; if (hdr->dxfer_len) @@ -442,11 +448,37 @@ error: return err; } + +/* Send basic block requests */ +static int __blk_send_generic(request_queue_t *q, struct gendisk *bd_disk, int cmd, int data) +{ + struct request *rq; + int err; + + rq = blk_get_request(q, WRITE, __GFP_WAIT); + rq->flags |= REQ_BLOCK_PC; + rq->data = NULL; + rq->data_len = 0; + rq->timeout = BLK_DEFAULT_TIMEOUT; + memset(rq->cmd, 0, sizeof(rq->cmd)); + rq->cmd[0] = cmd; + rq->cmd[4] = data; + rq->cmd_len = 6; + err = blk_execute_rq(q, bd_disk, rq, 0); + blk_put_request(rq); + + return err; +} + +static inline int blk_send_start_stop(request_queue_t *q, struct gendisk *bd_disk, int data) +{ + return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data); +} + int scsi_cmd_ioctl(struct file *file, struct gendisk *bd_disk, unsigned int cmd, void __user *arg) { request_queue_t *q; - struct request *rq; - int close = 0, err; + int err; q = bd_disk->queue; if (!q) @@ -564,19 +596,10 @@ int scsi_cmd_ioctl(struct file *file, struct gendisk *bd_disk, unsigned int cmd, err = sg_scsi_ioctl(file, q, bd_disk, arg); break; case CDROMCLOSETRAY: - close = 1; + err = blk_send_start_stop(q, bd_disk, 0x03); + break; case CDROMEJECT: - rq = blk_get_request(q, WRITE, __GFP_WAIT); - rq->flags |= REQ_BLOCK_PC; - rq->data = NULL; - rq->data_len = 0; - rq->timeout = BLK_DEFAULT_TIMEOUT; - memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->cmd[0] = GPCMD_START_STOP_UNIT; - rq->cmd[4] = 0x02 + (close != 0); - rq->cmd_len = 6; - err = blk_execute_rq(q, bd_disk, rq, 0); - blk_put_request(rq); + err = blk_send_start_stop(q, bd_disk, 0x02); break; default: err = -ENOTTY; |