linux/drivers/scsi/scsi_dh.c
<<
>>
Prefs
   1/*
   2 * SCSI device handler infrastruture.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms of the GNU General Public License as published by the
   6 * Free Software Foundation; either version 2 of the License, or (at your
   7 * option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12 * General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along
  15 * with this program; if not, write to the Free Software Foundation, Inc.,
  16 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17 *
  18 * Copyright IBM Corporation, 2007
  19 *      Authors:
  20 *               Chandra Seetharaman <sekharan@us.ibm.com>
  21 *               Mike Anderson <andmike@linux.vnet.ibm.com>
  22 */
  23
  24#include <linux/slab.h>
  25#include <linux/module.h>
  26#include <scsi/scsi_dh.h>
  27#include "scsi_priv.h"
  28
  29static DEFINE_SPINLOCK(list_lock);
  30static LIST_HEAD(scsi_dh_list);
  31
  32struct scsi_dh_blist {
  33        const char *vendor;
  34        const char *model;
  35        const char *driver;
  36};
  37
  38static const struct scsi_dh_blist scsi_dh_blist[] = {
  39        {"DGC", "RAID",                 "clariion" },
  40        {"DGC", "DISK",                 "clariion" },
  41        {"DGC", "VRAID",                "clariion" },
  42
  43        {"COMPAQ", "MSA1000 VOLUME",    "hp_sw" },
  44        {"COMPAQ", "HSV110",            "hp_sw" },
  45        {"HP", "HSV100",                "hp_sw"},
  46        {"DEC", "HSG80",                "hp_sw"},
  47
  48        {"IBM", "1722",                 "rdac", },
  49        {"IBM", "1724",                 "rdac", },
  50        {"IBM", "1726",                 "rdac", },
  51        {"IBM", "1742",                 "rdac", },
  52        {"IBM", "1745",                 "rdac", },
  53        {"IBM", "1746",                 "rdac", },
  54        {"IBM", "1813",                 "rdac", },
  55        {"IBM", "1814",                 "rdac", },
  56        {"IBM", "1815",                 "rdac", },
  57        {"IBM", "1818",                 "rdac", },
  58        {"IBM", "3526",                 "rdac", },
  59        {"SGI", "TP9",                  "rdac", },
  60        {"SGI", "IS",                   "rdac", },
  61        {"STK", "OPENstorage D280",     "rdac", },
  62        {"STK", "FLEXLINE 380",         "rdac", },
  63        {"SUN", "CSM",                  "rdac", },
  64        {"SUN", "LCSM100",              "rdac", },
  65        {"SUN", "STK6580_6780",         "rdac", },
  66        {"SUN", "SUN_6180",             "rdac", },
  67        {"SUN", "ArrayStorage",         "rdac", },
  68        {"DELL", "MD3",                 "rdac", },
  69        {"NETAPP", "INF-01-00",         "rdac", },
  70        {"LSI", "INF-01-00",            "rdac", },
  71        {"ENGENIO", "INF-01-00",        "rdac", },
  72        {NULL, NULL,                    NULL },
  73};
  74
  75static const char *
  76scsi_dh_find_driver(struct scsi_device *sdev)
  77{
  78        const struct scsi_dh_blist *b;
  79
  80        if (scsi_device_tpgs(sdev))
  81                return "alua";
  82
  83        for (b = scsi_dh_blist; b->vendor; b++) {
  84                if (!strncmp(sdev->vendor, b->vendor, strlen(b->vendor)) &&
  85                    !strncmp(sdev->model, b->model, strlen(b->model))) {
  86                        return b->driver;
  87                }
  88        }
  89        return NULL;
  90}
  91
  92
  93static struct scsi_device_handler *__scsi_dh_lookup(const char *name)
  94{
  95        struct scsi_device_handler *tmp, *found = NULL;
  96
  97        spin_lock(&list_lock);
  98        list_for_each_entry(tmp, &scsi_dh_list, list) {
  99                if (!strncmp(tmp->name, name, strlen(tmp->name))) {
 100                        found = tmp;
 101                        break;
 102                }
 103        }
 104        spin_unlock(&list_lock);
 105        return found;
 106}
 107
 108static struct scsi_device_handler *scsi_dh_lookup(const char *name)
 109{
 110        struct scsi_device_handler *dh;
 111
 112        dh = __scsi_dh_lookup(name);
 113        if (!dh) {
 114                request_module("scsi_dh_%s", name);
 115                dh = __scsi_dh_lookup(name);
 116        }
 117
 118        return dh;
 119}
 120
 121/*
 122 * scsi_dh_handler_attach - Attach a device handler to a device
 123 * @sdev - SCSI device the device handler should attach to
 124 * @scsi_dh - The device handler to attach
 125 */
 126static int scsi_dh_handler_attach(struct scsi_device *sdev,
 127                                  struct scsi_device_handler *scsi_dh)
 128{
 129        int error;
 130
 131        if (!try_module_get(scsi_dh->module))
 132                return -EINVAL;
 133
 134        error = scsi_dh->attach(sdev);
 135        if (error) {
 136                sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%d)\n",
 137                            scsi_dh->name, error);
 138                module_put(scsi_dh->module);
 139        } else
 140                sdev->handler = scsi_dh;
 141
 142        return error;
 143}
 144
 145/*
 146 * scsi_dh_handler_detach - Detach a device handler from a device
 147 * @sdev - SCSI device the device handler should be detached from
 148 */
 149static void scsi_dh_handler_detach(struct scsi_device *sdev)
 150{
 151        sdev->handler->detach(sdev);
 152        sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", sdev->handler->name);
 153        module_put(sdev->handler->module);
 154}
 155
 156/*
 157 * Functions for sysfs attribute 'dh_state'
 158 */
 159static ssize_t
 160store_dh_state(struct device *dev, struct device_attribute *attr,
 161               const char *buf, size_t count)
 162{
 163        struct scsi_device *sdev = to_scsi_device(dev);
 164        struct scsi_device_handler *scsi_dh;
 165        int err = -EINVAL;
 166
 167        if (sdev->sdev_state == SDEV_CANCEL ||
 168            sdev->sdev_state == SDEV_DEL)
 169                return -ENODEV;
 170
 171        if (!sdev->handler) {
 172                /*
 173                 * Attach to a device handler
 174                 */
 175                scsi_dh = scsi_dh_lookup(buf);
 176                if (!scsi_dh)
 177                        return err;
 178                err = scsi_dh_handler_attach(sdev, scsi_dh);
 179        } else {
 180                if (!strncmp(buf, "detach", 6)) {
 181                        /*
 182                         * Detach from a device handler
 183                         */
 184                        sdev_printk(KERN_WARNING, sdev,
 185                                    "can't detach handler %s.\n",
 186                                    sdev->handler->name);
 187                        err = -EINVAL;
 188                } else if (!strncmp(buf, "activate", 8)) {
 189                        /*
 190                         * Activate a device handler
 191                         */
 192                        if (sdev->handler->activate)
 193                                err = sdev->handler->activate(sdev, NULL, NULL);
 194                        else
 195                                err = 0;
 196                }
 197        }
 198
 199        return err<0?err:count;
 200}
 201
 202static ssize_t
 203show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
 204{
 205        struct scsi_device *sdev = to_scsi_device(dev);
 206
 207        if (!sdev->handler)
 208                return snprintf(buf, 20, "detached\n");
 209
 210        return snprintf(buf, 20, "%s\n", sdev->handler->name);
 211}
 212
 213static struct device_attribute scsi_dh_state_attr =
 214        __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
 215               store_dh_state);
 216
 217int scsi_dh_add_device(struct scsi_device *sdev)
 218{
 219        struct scsi_device_handler *devinfo = NULL;
 220        const char *drv;
 221        int err;
 222
 223        err = device_create_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
 224        if (err)
 225                return err;
 226
 227        drv = scsi_dh_find_driver(sdev);
 228        if (drv)
 229                devinfo = __scsi_dh_lookup(drv);
 230        if (devinfo)
 231                err = scsi_dh_handler_attach(sdev, devinfo);
 232        return err;
 233}
 234
 235void scsi_dh_release_device(struct scsi_device *sdev)
 236{
 237        if (sdev->handler)
 238                scsi_dh_handler_detach(sdev);
 239}
 240
 241void scsi_dh_remove_device(struct scsi_device *sdev)
 242{
 243        device_remove_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
 244}
 245
 246/*
 247 * scsi_register_device_handler - register a device handler personality
 248 *      module.
 249 * @scsi_dh - device handler to be registered.
 250 *
 251 * Returns 0 on success, -EBUSY if handler already registered.
 252 */
 253int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
 254{
 255        if (__scsi_dh_lookup(scsi_dh->name))
 256                return -EBUSY;
 257
 258        if (!scsi_dh->attach || !scsi_dh->detach)
 259                return -EINVAL;
 260
 261        spin_lock(&list_lock);
 262        list_add(&scsi_dh->list, &scsi_dh_list);
 263        spin_unlock(&list_lock);
 264
 265        printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
 266
 267        return SCSI_DH_OK;
 268}
 269EXPORT_SYMBOL_GPL(scsi_register_device_handler);
 270
 271/*
 272 * scsi_unregister_device_handler - register a device handler personality
 273 *      module.
 274 * @scsi_dh - device handler to be unregistered.
 275 *
 276 * Returns 0 on success, -ENODEV if handler not registered.
 277 */
 278int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
 279{
 280        if (!__scsi_dh_lookup(scsi_dh->name))
 281                return -ENODEV;
 282
 283        spin_lock(&list_lock);
 284        list_del(&scsi_dh->list);
 285        spin_unlock(&list_lock);
 286        printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
 287
 288        return SCSI_DH_OK;
 289}
 290EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
 291
 292static struct scsi_device *get_sdev_from_queue(struct request_queue *q)
 293{
 294        struct scsi_device *sdev;
 295        unsigned long flags;
 296
 297        spin_lock_irqsave(q->queue_lock, flags);
 298        sdev = q->queuedata;
 299        if (!sdev || !get_device(&sdev->sdev_gendev))
 300                sdev = NULL;
 301        spin_unlock_irqrestore(q->queue_lock, flags);
 302
 303        return sdev;
 304}
 305
 306/*
 307 * scsi_dh_activate - activate the path associated with the scsi_device
 308 *      corresponding to the given request queue.
 309 *     Returns immediately without waiting for activation to be completed.
 310 * @q    - Request queue that is associated with the scsi_device to be
 311 *         activated.
 312 * @fn   - Function to be called upon completion of the activation.
 313 *         Function fn is called with data (below) and the error code.
 314 *         Function fn may be called from the same calling context. So,
 315 *         do not hold the lock in the caller which may be needed in fn.
 316 * @data - data passed to the function fn upon completion.
 317 *
 318 */
 319int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
 320{
 321        struct scsi_device *sdev;
 322        int err = SCSI_DH_NOSYS;
 323
 324        sdev = get_sdev_from_queue(q);
 325        if (!sdev) {
 326                if (fn)
 327                        fn(data, err);
 328                return err;
 329        }
 330
 331        if (!sdev->handler)
 332                goto out_fn;
 333        err = SCSI_DH_NOTCONN;
 334        if (sdev->sdev_state == SDEV_CANCEL ||
 335            sdev->sdev_state == SDEV_DEL)
 336                goto out_fn;
 337
 338        err = SCSI_DH_DEV_OFFLINED;
 339        if (sdev->sdev_state == SDEV_OFFLINE)
 340                goto out_fn;
 341
 342        if (sdev->handler->activate)
 343                err = sdev->handler->activate(sdev, fn, data);
 344
 345out_put_device:
 346        put_device(&sdev->sdev_gendev);
 347        return err;
 348
 349out_fn:
 350        if (fn)
 351                fn(data, err);
 352        goto out_put_device;
 353}
 354EXPORT_SYMBOL_GPL(scsi_dh_activate);
 355
 356/*
 357 * scsi_dh_set_params - set the parameters for the device as per the
 358 *      string specified in params.
 359 * @q - Request queue that is associated with the scsi_device for
 360 *      which the parameters to be set.
 361 * @params - parameters in the following format
 362 *      "no_of_params\0param1\0param2\0param3\0...\0"
 363 *      for example, string for 2 parameters with value 10 and 21
 364 *      is specified as "2\010\021\0".
 365 */
 366int scsi_dh_set_params(struct request_queue *q, const char *params)
 367{
 368        struct scsi_device *sdev;
 369        int err = -SCSI_DH_NOSYS;
 370
 371        sdev = get_sdev_from_queue(q);
 372        if (!sdev)
 373                return err;
 374
 375        if (sdev->handler && sdev->handler->set_params)
 376                err = sdev->handler->set_params(sdev, params);
 377        put_device(&sdev->sdev_gendev);
 378        return err;
 379}
 380EXPORT_SYMBOL_GPL(scsi_dh_set_params);
 381
 382/*
 383 * scsi_dh_attach - Attach device handler
 384 * @q - Request queue that is associated with the scsi_device
 385 *      the handler should be attached to
 386 * @name - name of the handler to attach
 387 */
 388int scsi_dh_attach(struct request_queue *q, const char *name)
 389{
 390        struct scsi_device *sdev;
 391        struct scsi_device_handler *scsi_dh;
 392        int err = 0;
 393
 394        sdev = get_sdev_from_queue(q);
 395        if (!sdev)
 396                return -ENODEV;
 397
 398        scsi_dh = scsi_dh_lookup(name);
 399        if (!scsi_dh) {
 400                err = -EINVAL;
 401                goto out_put_device;
 402        }
 403
 404        if (sdev->handler) {
 405                if (sdev->handler != scsi_dh)
 406                        err = -EBUSY;
 407                goto out_put_device;
 408        }
 409
 410        err = scsi_dh_handler_attach(sdev, scsi_dh);
 411
 412out_put_device:
 413        put_device(&sdev->sdev_gendev);
 414        return err;
 415}
 416EXPORT_SYMBOL_GPL(scsi_dh_attach);
 417
 418/*
 419 * scsi_dh_attached_handler_name - Get attached device handler's name
 420 * @q - Request queue that is associated with the scsi_device
 421 *      that may have a device handler attached
 422 * @gfp - the GFP mask used in the kmalloc() call when allocating memory
 423 *
 424 * Returns name of attached handler, NULL if no handler is attached.
 425 * Caller must take care to free the returned string.
 426 */
 427const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
 428{
 429        struct scsi_device *sdev;
 430        const char *handler_name = NULL;
 431
 432        sdev = get_sdev_from_queue(q);
 433        if (!sdev)
 434                return NULL;
 435
 436        if (sdev->handler)
 437                handler_name = kstrdup(sdev->handler->name, gfp);
 438        put_device(&sdev->sdev_gendev);
 439        return handler_name;
 440}
 441EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name);
 442