diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/elevator.c | 34 | ||||
-rw-r--r-- | block/genhd.c | 106 | ||||
-rw-r--r-- | block/ioctl.c | 24 | ||||
-rw-r--r-- | block/ll_rw_blk.c | 131 | ||||
-rw-r--r-- | block/scsi_ioctl.c | 14 |
5 files changed, 244 insertions, 65 deletions
diff --git a/block/elevator.c b/block/elevator.c index 39dcccc82ad..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) @@ -611,23 +617,23 @@ void elv_completed_request(request_queue_t *q, struct request *rq) * request is released from the driver, io must be done */ if (blk_account_rq(rq)) { - struct request *first_rq = list_entry_rq(q->queue_head.next); - q->in_flight--; + 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 (q->ordseq && q->in_flight == 0 && + /* + * 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); } - - if (blk_sorted_rq(rq) && e->ops->elevator_completed_req_fn) - e->ops->elevator_completed_req_fn(q, rq); } } diff --git a/block/genhd.c b/block/genhd.c index f1ed83f3f08..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) { 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 91d3b4828c4..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 @@ -62,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) @@ -207,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 @@ -270,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; @@ -286,6 +297,7 @@ 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; } /** @@ -2735,30 +2747,6 @@ 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) -{ - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - attempt_back_merge(q, rq); - spin_unlock_irqrestore(q->queue_lock, flags); -} - -EXPORT_SYMBOL(blk_attempt_remerge); - static void init_request_from_bio(struct request *req, struct bio *bio) { req->flags |= REQ_CMD; @@ -3287,6 +3275,87 @@ 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, int uptodate) @@ -3364,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"); @@ -3377,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 c2ac36dfe4f..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> @@ -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)) |