Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (C) 2004-2005 IBM Corp.  All Rights Reserved.
0003  * Copyright (C) 2006-2009 NEC Corporation.
0004  *
0005  * dm-queue-length.c
0006  *
0007  * Module Author: Stefan Bader, IBM
0008  * Modified by: Kiyoshi Ueda, NEC
0009  *
0010  * This file is released under the GPL.
0011  *
0012  * queue-length path selector - choose a path with the least number of
0013  * in-flight I/Os.
0014  */
0015 
0016 #include "dm.h"
0017 #include "dm-path-selector.h"
0018 
0019 #include <linux/slab.h>
0020 #include <linux/ctype.h>
0021 #include <linux/errno.h>
0022 #include <linux/module.h>
0023 #include <linux/atomic.h>
0024 
0025 #define DM_MSG_PREFIX   "multipath queue-length"
0026 #define QL_MIN_IO   1
0027 #define QL_VERSION  "0.2.0"
0028 
0029 struct selector {
0030     struct list_head    valid_paths;
0031     struct list_head    failed_paths;
0032     spinlock_t lock;
0033 };
0034 
0035 struct path_info {
0036     struct list_head    list;
0037     struct dm_path      *path;
0038     unsigned        repeat_count;
0039     atomic_t        qlen;   /* the number of in-flight I/Os */
0040 };
0041 
0042 static struct selector *alloc_selector(void)
0043 {
0044     struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
0045 
0046     if (s) {
0047         INIT_LIST_HEAD(&s->valid_paths);
0048         INIT_LIST_HEAD(&s->failed_paths);
0049         spin_lock_init(&s->lock);
0050     }
0051 
0052     return s;
0053 }
0054 
0055 static int ql_create(struct path_selector *ps, unsigned argc, char **argv)
0056 {
0057     struct selector *s = alloc_selector();
0058 
0059     if (!s)
0060         return -ENOMEM;
0061 
0062     ps->context = s;
0063     return 0;
0064 }
0065 
0066 static void ql_free_paths(struct list_head *paths)
0067 {
0068     struct path_info *pi, *next;
0069 
0070     list_for_each_entry_safe(pi, next, paths, list) {
0071         list_del(&pi->list);
0072         kfree(pi);
0073     }
0074 }
0075 
0076 static void ql_destroy(struct path_selector *ps)
0077 {
0078     struct selector *s = ps->context;
0079 
0080     ql_free_paths(&s->valid_paths);
0081     ql_free_paths(&s->failed_paths);
0082     kfree(s);
0083     ps->context = NULL;
0084 }
0085 
0086 static int ql_status(struct path_selector *ps, struct dm_path *path,
0087              status_type_t type, char *result, unsigned maxlen)
0088 {
0089     unsigned sz = 0;
0090     struct path_info *pi;
0091 
0092     /* When called with NULL path, return selector status/args. */
0093     if (!path)
0094         DMEMIT("0 ");
0095     else {
0096         pi = path->pscontext;
0097 
0098         switch (type) {
0099         case STATUSTYPE_INFO:
0100             DMEMIT("%d ", atomic_read(&pi->qlen));
0101             break;
0102         case STATUSTYPE_TABLE:
0103             DMEMIT("%u ", pi->repeat_count);
0104             break;
0105         case STATUSTYPE_IMA:
0106             *result = '\0';
0107             break;
0108         }
0109     }
0110 
0111     return sz;
0112 }
0113 
0114 static int ql_add_path(struct path_selector *ps, struct dm_path *path,
0115                int argc, char **argv, char **error)
0116 {
0117     struct selector *s = ps->context;
0118     struct path_info *pi;
0119     unsigned repeat_count = QL_MIN_IO;
0120     char dummy;
0121     unsigned long flags;
0122 
0123     /*
0124      * Arguments: [<repeat_count>]
0125      *  <repeat_count>: The number of I/Os before switching path.
0126      *          If not given, default (QL_MIN_IO) is used.
0127      */
0128     if (argc > 1) {
0129         *error = "queue-length ps: incorrect number of arguments";
0130         return -EINVAL;
0131     }
0132 
0133     if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
0134         *error = "queue-length ps: invalid repeat count";
0135         return -EINVAL;
0136     }
0137 
0138     if (repeat_count > 1) {
0139         DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");
0140         repeat_count = 1;
0141     }
0142 
0143     /* Allocate the path information structure */
0144     pi = kmalloc(sizeof(*pi), GFP_KERNEL);
0145     if (!pi) {
0146         *error = "queue-length ps: Error allocating path information";
0147         return -ENOMEM;
0148     }
0149 
0150     pi->path = path;
0151     pi->repeat_count = repeat_count;
0152     atomic_set(&pi->qlen, 0);
0153 
0154     path->pscontext = pi;
0155 
0156     spin_lock_irqsave(&s->lock, flags);
0157     list_add_tail(&pi->list, &s->valid_paths);
0158     spin_unlock_irqrestore(&s->lock, flags);
0159 
0160     return 0;
0161 }
0162 
0163 static void ql_fail_path(struct path_selector *ps, struct dm_path *path)
0164 {
0165     struct selector *s = ps->context;
0166     struct path_info *pi = path->pscontext;
0167     unsigned long flags;
0168 
0169     spin_lock_irqsave(&s->lock, flags);
0170     list_move(&pi->list, &s->failed_paths);
0171     spin_unlock_irqrestore(&s->lock, flags);
0172 }
0173 
0174 static int ql_reinstate_path(struct path_selector *ps, struct dm_path *path)
0175 {
0176     struct selector *s = ps->context;
0177     struct path_info *pi = path->pscontext;
0178     unsigned long flags;
0179 
0180     spin_lock_irqsave(&s->lock, flags);
0181     list_move_tail(&pi->list, &s->valid_paths);
0182     spin_unlock_irqrestore(&s->lock, flags);
0183 
0184     return 0;
0185 }
0186 
0187 /*
0188  * Select a path having the minimum number of in-flight I/Os
0189  */
0190 static struct dm_path *ql_select_path(struct path_selector *ps, size_t nr_bytes)
0191 {
0192     struct selector *s = ps->context;
0193     struct path_info *pi = NULL, *best = NULL;
0194     struct dm_path *ret = NULL;
0195     unsigned long flags;
0196 
0197     spin_lock_irqsave(&s->lock, flags);
0198     if (list_empty(&s->valid_paths))
0199         goto out;
0200 
0201     list_for_each_entry(pi, &s->valid_paths, list) {
0202         if (!best ||
0203             (atomic_read(&pi->qlen) < atomic_read(&best->qlen)))
0204             best = pi;
0205 
0206         if (!atomic_read(&best->qlen))
0207             break;
0208     }
0209 
0210     if (!best)
0211         goto out;
0212 
0213     /* Move most recently used to least preferred to evenly balance. */
0214     list_move_tail(&best->list, &s->valid_paths);
0215 
0216     ret = best->path;
0217 out:
0218     spin_unlock_irqrestore(&s->lock, flags);
0219     return ret;
0220 }
0221 
0222 static int ql_start_io(struct path_selector *ps, struct dm_path *path,
0223                size_t nr_bytes)
0224 {
0225     struct path_info *pi = path->pscontext;
0226 
0227     atomic_inc(&pi->qlen);
0228 
0229     return 0;
0230 }
0231 
0232 static int ql_end_io(struct path_selector *ps, struct dm_path *path,
0233              size_t nr_bytes, u64 start_time)
0234 {
0235     struct path_info *pi = path->pscontext;
0236 
0237     atomic_dec(&pi->qlen);
0238 
0239     return 0;
0240 }
0241 
0242 static struct path_selector_type ql_ps = {
0243     .name       = "queue-length",
0244     .module     = THIS_MODULE,
0245     .table_args = 1,
0246     .info_args  = 1,
0247     .create     = ql_create,
0248     .destroy    = ql_destroy,
0249     .status     = ql_status,
0250     .add_path   = ql_add_path,
0251     .fail_path  = ql_fail_path,
0252     .reinstate_path = ql_reinstate_path,
0253     .select_path    = ql_select_path,
0254     .start_io   = ql_start_io,
0255     .end_io     = ql_end_io,
0256 };
0257 
0258 static int __init dm_ql_init(void)
0259 {
0260     int r = dm_register_path_selector(&ql_ps);
0261 
0262     if (r < 0)
0263         DMERR("register failed %d", r);
0264 
0265     DMINFO("version " QL_VERSION " loaded");
0266 
0267     return r;
0268 }
0269 
0270 static void __exit dm_ql_exit(void)
0271 {
0272     int r = dm_unregister_path_selector(&ql_ps);
0273 
0274     if (r < 0)
0275         DMERR("unregister failed %d", r);
0276 }
0277 
0278 module_init(dm_ql_init);
0279 module_exit(dm_ql_exit);
0280 
0281 MODULE_AUTHOR("Stefan Bader <Stefan.Bader at de.ibm.com>");
0282 MODULE_DESCRIPTION(
0283     "(C) Copyright IBM Corp. 2004,2005   All Rights Reserved.\n"
0284     DM_NAME " path selector to balance the number of in-flight I/Os"
0285 );
0286 MODULE_LICENSE("GPL");