linux-old/drivers/block/elevator.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/block/elevator.c
   3 *
   4 *  Block device elevator/IO-scheduler.
   5 *
   6 *  Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
   7 *
   8 * 30042000 Jens Axboe <axboe@suse.de> :
   9 *
  10 * Split the elevator a bit so that it is possible to choose a different
  11 * one or even write a new "plug in". There are three pieces:
  12 * - elevator_fn, inserts a new request in the queue list
  13 * - elevator_merge_fn, decides whether a new buffer can be merged with
  14 *   an existing request
  15 * - elevator_dequeue_fn, called when a request is taken off the active list
  16 *
  17 * 20082000 Dave Jones <davej@suse.de> :
  18 * Removed tests for max-bomb-segments, which was breaking elvtune
  19 *  when run without -bN
  20 *
  21 */
  22
  23#include <linux/fs.h>
  24#include <linux/blkdev.h>
  25#include <linux/elevator.h>
  26#include <linux/blk.h>
  27#include <linux/module.h>
  28#include <asm/uaccess.h>
  29
  30/*
  31 * This is a bit tricky. It's given that bh and rq are for the same
  32 * device, but the next request might of course not be. Run through
  33 * the tests below to check if we want to insert here if we can't merge
  34 * bh into an existing request
  35 */
  36inline int bh_rq_in_between(struct buffer_head *bh, struct request *rq,
  37                            struct list_head *head)
  38{
  39        struct list_head *next;
  40        struct request *next_rq;
  41
  42        next = rq->queue.next;
  43        if (next == head)
  44                return 0;
  45
  46        /*
  47         * if the device is different (usually on a different partition),
  48         * just check if bh is after rq
  49         */
  50        next_rq = blkdev_entry_to_request(next);
  51        if (next_rq->rq_dev != rq->rq_dev)
  52                return bh->b_rsector > rq->sector;
  53
  54        /*
  55         * ok, rq, next_rq and bh are on the same device. if bh is in between
  56         * the two, this is the sweet spot
  57         */
  58        if (bh->b_rsector < next_rq->sector && bh->b_rsector > rq->sector)
  59                return 1;
  60
  61        /*
  62         * next_rq is ordered wrt rq, but bh is not in between the two
  63         */
  64        if (next_rq->sector > rq->sector)
  65                return 0;
  66
  67        /*
  68         * next_rq and rq not ordered, if we happen to be either before
  69         * next_rq or after rq insert here anyway
  70         */
  71        if (bh->b_rsector > rq->sector || bh->b_rsector < next_rq->sector)
  72                return 1;
  73
  74        return 0;
  75}
  76
  77
  78int elevator_linus_merge(request_queue_t *q, struct request **req,
  79                         struct list_head * head,
  80                         struct buffer_head *bh, int rw,
  81                         int max_sectors)
  82{
  83        struct list_head *entry = &q->queue_head;
  84        unsigned int count = bh->b_size >> 9, ret = ELEVATOR_NO_MERGE;
  85        struct request *__rq;
  86        int backmerge_only = 0;
  87
  88        while (!backmerge_only && (entry = entry->prev) != head) {
  89                __rq = blkdev_entry_to_request(entry);
  90
  91                /*
  92                 * we can't insert beyond a zero sequence point
  93                 */
  94                if (__rq->elevator_sequence <= 0)
  95                        backmerge_only = 1;
  96
  97                if (__rq->waiting)
  98                        continue;
  99                if (__rq->rq_dev != bh->b_rdev)
 100                        continue;
 101                if (!*req && bh_rq_in_between(bh, __rq, &q->queue_head) && !backmerge_only)
 102                        *req = __rq;
 103                if (__rq->cmd != rw)
 104                        continue;
 105                if (__rq->nr_sectors + count > max_sectors)
 106                        continue;
 107                if (__rq->sector + __rq->nr_sectors == bh->b_rsector) {
 108                        ret = ELEVATOR_BACK_MERGE;
 109                        *req = __rq;
 110                        break;
 111                } else if (__rq->sector - count == bh->b_rsector && !backmerge_only) {
 112                        ret = ELEVATOR_FRONT_MERGE;
 113                        __rq->elevator_sequence--;
 114                        *req = __rq;
 115                        break;
 116                }
 117        }
 118
 119        /*
 120         * account merge (ret != 0, cost is 1) or seeky insert (*req is set,
 121         * cost is ELV_LINUS_SEEK_COST
 122         */
 123        if (*req) {
 124                int scan_cost = ret ? 1 : ELV_LINUS_SEEK_COST;
 125                struct list_head *entry = &(*req)->queue;
 126
 127                while ((entry = entry->next) != &q->queue_head) {
 128                        __rq = blkdev_entry_to_request(entry);
 129                        __rq->elevator_sequence -= scan_cost;
 130                }
 131        }
 132
 133        return ret;
 134}
 135
 136void elevator_linus_merge_req(struct request *req, struct request *next)
 137{
 138        if (next->elevator_sequence < req->elevator_sequence)
 139                req->elevator_sequence = next->elevator_sequence;
 140}
 141
 142/*
 143 * See if we can find a request that this buffer can be coalesced with.
 144 */
 145int elevator_noop_merge(request_queue_t *q, struct request **req,
 146                        struct list_head * head,
 147                        struct buffer_head *bh, int rw,
 148                        int max_sectors)
 149{
 150        struct list_head *entry;
 151        unsigned int count = bh->b_size >> 9;
 152
 153        if (list_empty(&q->queue_head))
 154                return ELEVATOR_NO_MERGE;
 155
 156        entry = &q->queue_head;
 157        while ((entry = entry->prev) != head) {
 158                struct request *__rq = blkdev_entry_to_request(entry);
 159
 160                if (__rq->cmd != rw)
 161                        continue;
 162                if (__rq->rq_dev != bh->b_rdev)
 163                        continue;
 164                if (__rq->nr_sectors + count > max_sectors)
 165                        continue;
 166                if (__rq->waiting)
 167                        continue;
 168                if (__rq->sector + __rq->nr_sectors == bh->b_rsector) {
 169                        *req = __rq;
 170                        return ELEVATOR_BACK_MERGE;
 171                } else if (__rq->sector - count == bh->b_rsector) {
 172                        *req = __rq;
 173                        return ELEVATOR_FRONT_MERGE;
 174                }
 175        }
 176
 177        *req = blkdev_entry_to_request(q->queue_head.prev);
 178        return ELEVATOR_NO_MERGE;
 179}
 180
 181void elevator_noop_merge_req(struct request *req, struct request *next) {}
 182
 183int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg)
 184{
 185        blkelv_ioctl_arg_t output;
 186
 187        output.queue_ID                 = elevator->queue_ID;
 188        output.read_latency             = elevator->read_latency;
 189        output.write_latency            = elevator->write_latency;
 190        output.max_bomb_segments        = 0;
 191
 192        if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t)))
 193                return -EFAULT;
 194
 195        return 0;
 196}
 197
 198int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg)
 199{
 200        blkelv_ioctl_arg_t input;
 201
 202        if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t)))
 203                return -EFAULT;
 204
 205        if (input.read_latency < 0)
 206                return -EINVAL;
 207        if (input.write_latency < 0)
 208                return -EINVAL;
 209
 210        elevator->read_latency          = input.read_latency;
 211        elevator->write_latency         = input.write_latency;
 212        return 0;
 213}
 214
 215void elevator_init(elevator_t * elevator, elevator_t type)
 216{
 217        static unsigned int queue_ID;
 218
 219        *elevator = type;
 220        elevator->queue_ID = queue_ID++;
 221}
 222
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.