linux/drivers/scsi/device_handler/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
  32static struct scsi_device_handler *get_device_handler(const char *name)
  33{
  34        struct scsi_device_handler *tmp, *found = NULL;
  35
  36        spin_lock(&list_lock);
  37        list_for_each_entry(tmp, &scsi_dh_list, list) {
  38                if (!strncmp(tmp->name, name, strlen(tmp->name))) {
  39                        found = tmp;
  40                        break;
  41                }
  42        }
  43        spin_unlock(&list_lock);
  44        return found;
  45}
  46
  47/*
  48 * device_handler_match_function - Match a device handler to a device
  49 * @sdev - SCSI device to be tested
  50 *
  51 * Tests @sdev against the match function of all registered device_handler.
  52 * Returns the found device handler or NULL if not found.
  53 */
  54static struct scsi_device_handler *
  55device_handler_match_function(struct scsi_device *sdev)
  56{
  57        struct scsi_device_handler *tmp_dh, *found_dh = NULL;
  58
  59        spin_lock(&list_lock);
  60        list_for_each_entry(tmp_dh, &scsi_dh_list, list) {
  61                if (tmp_dh->match && tmp_dh->match(sdev)) {
  62                        found_dh = tmp_dh;
  63                        break;
  64                }
  65        }
  66        spin_unlock(&list_lock);
  67        return found_dh;
  68}
  69
  70/*
  71 * device_handler_match - Attach a device handler to a device
  72 * @scsi_dh - The device handler to match against or NULL
  73 * @sdev - SCSI device to be tested against @scsi_dh
  74 *
  75 * Tests @sdev against the device handler @scsi_dh or against
  76 * all registered device_handler if @scsi_dh == NULL.
  77 * Returns the found device handler or NULL if not found.
  78 */
  79static struct scsi_device_handler *
  80device_handler_match(struct scsi_device_handler *scsi_dh,
  81                     struct scsi_device *sdev)
  82{
  83        struct scsi_device_handler *found_dh;
  84
  85        found_dh = device_handler_match_function(sdev);
  86
  87        if (scsi_dh && found_dh != scsi_dh)
  88                found_dh = NULL;
  89
  90        return found_dh;
  91}
  92
  93/*
  94 * scsi_dh_handler_attach - Attach a device handler to a device
  95 * @sdev - SCSI device the device handler should attach to
  96 * @scsi_dh - The device handler to attach
  97 */
  98static int scsi_dh_handler_attach(struct scsi_device *sdev,
  99                                  struct scsi_device_handler *scsi_dh)
 100{
 101        int err = 0;
 102
 103        if (sdev->scsi_dh_data) {
 104                if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
 105                        err = -EBUSY;
 106                else
 107                        kref_get(&sdev->scsi_dh_data->kref);
 108        } else if (scsi_dh->attach) {
 109                err = scsi_dh->attach(sdev);
 110                if (!err) {
 111                        kref_init(&sdev->scsi_dh_data->kref);
 112                        sdev->scsi_dh_data->sdev = sdev;
 113                }
 114        }
 115        return err;
 116}
 117
 118static void __detach_handler (struct kref *kref)
 119{
 120        struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref);
 121        scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev);
 122}
 123
 124/*
 125 * scsi_dh_handler_detach - Detach a device handler from a device
 126 * @sdev - SCSI device the device handler should be detached from
 127 * @scsi_dh - Device handler to be detached
 128 *
 129 * Detach from a device handler. If a device handler is specified,
 130 * only detach if the currently attached handler matches @scsi_dh.
 131 */
 132static void scsi_dh_handler_detach(struct scsi_device *sdev,
 133                                   struct scsi_device_handler *scsi_dh)
 134{
 135        if (!sdev->scsi_dh_data)
 136                return;
 137
 138        if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh)
 139                return;
 140
 141        if (!scsi_dh)
 142                scsi_dh = sdev->scsi_dh_data->scsi_dh;
 143
 144        if (scsi_dh && scsi_dh->detach)
 145                kref_put(&sdev->scsi_dh_data->kref, __detach_handler);
 146}
 147
 148/*
 149 * Functions for sysfs attribute 'dh_state'
 150 */
 151static ssize_t
 152store_dh_state(struct device *dev, struct device_attribute *attr,
 153               const char *buf, size_t count)
 154{
 155        struct scsi_device *sdev = to_scsi_device(dev);
 156        struct scsi_device_handler *scsi_dh;
 157        int err = -EINVAL;
 158
 159        if (sdev->sdev_state == SDEV_CANCEL ||
 160            sdev->sdev_state == SDEV_DEL)
 161                return -ENODEV;
 162
 163        if (!sdev->scsi_dh_data) {
 164                /*
 165                 * Attach to a device handler
 166                 */
 167                if (!(scsi_dh = get_device_handler(buf)))
 168                        return err;
 169                err = scsi_dh_handler_attach(sdev, scsi_dh);
 170        } else {
 171                scsi_dh = sdev->scsi_dh_data->scsi_dh;
 172                if (!strncmp(buf, "detach", 6)) {
 173                        /*
 174                         * Detach from a device handler
 175                         */
 176                        scsi_dh_handler_detach(sdev, scsi_dh);
 177                        err = 0;
 178                } else if (!strncmp(buf, "activate", 8)) {
 179                        /*
 180                         * Activate a device handler
 181                         */
 182                        if (scsi_dh->activate)
 183                                err = scsi_dh->activate(sdev, NULL, NULL);
 184                        else
 185                                err = 0;
 186                }
 187        }
 188
 189        return err<0?err:count;
 190}
 191
 192static ssize_t
 193show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
 194{
 195        struct scsi_device *sdev = to_scsi_device(dev);
 196
 197        if (!sdev->scsi_dh_data)
 198                return snprintf(buf, 20, "detached\n");
 199
 200        return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name);
 201}
 202
 203static struct device_attribute scsi_dh_state_attr =
 204        __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
 205               store_dh_state);
 206
 207/*
 208 * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh
 209 */
 210static int scsi_dh_sysfs_attr_add(struct device *dev, void *data)
 211{
 212        struct scsi_device *sdev;
 213        int err;
 214
 215        if (!scsi_is_sdev_device(dev))
 216                return 0;
 217
 218        sdev = to_scsi_device(dev);
 219
 220        err = device_create_file(&sdev->sdev_gendev,
 221                                 &scsi_dh_state_attr);
 222
 223        return 0;
 224}
 225
 226/*
 227 * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh
 228 */
 229static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data)
 230{
 231        struct scsi_device *sdev;
 232
 233        if (!scsi_is_sdev_device(dev))
 234                return 0;
 235
 236        sdev = to_scsi_device(dev);
 237
 238        device_remove_file(&sdev->sdev_gendev,
 239                           &scsi_dh_state_attr);
 240
 241        return 0;
 242}
 243
 244/*
 245 * scsi_dh_notifier - notifier chain callback
 246 */
 247static int scsi_dh_notifier(struct notifier_block *nb,
 248                            unsigned long action, void *data)
 249{
 250        struct device *dev = data;
 251        struct scsi_device *sdev;
 252        int err = 0;
 253        struct scsi_device_handler *devinfo = NULL;
 254
 255        if (!scsi_is_sdev_device(dev))
 256                return 0;
 257
 258        sdev = to_scsi_device(dev);
 259
 260        if (action == BUS_NOTIFY_ADD_DEVICE) {
 261                err = device_create_file(dev, &scsi_dh_state_attr);
 262                /* don't care about err */
 263                devinfo = device_handler_match(NULL, sdev);
 264                if (devinfo)
 265                        err = scsi_dh_handler_attach(sdev, devinfo);
 266        } else if (action == BUS_NOTIFY_DEL_DEVICE) {
 267                device_remove_file(dev, &scsi_dh_state_attr);
 268                scsi_dh_handler_detach(sdev, NULL);
 269        }
 270        return err;
 271}
 272
 273/*
 274 * scsi_dh_notifier_add - Callback for scsi_register_device_handler
 275 */
 276static int scsi_dh_notifier_add(struct device *dev, void *data)
 277{
 278        struct scsi_device_handler *scsi_dh = data;
 279        struct scsi_device *sdev;
 280
 281        if (!scsi_is_sdev_device(dev))
 282                return 0;
 283
 284        if (!get_device(dev))
 285                return 0;
 286
 287        sdev = to_scsi_device(dev);
 288
 289        if (device_handler_match(scsi_dh, sdev))
 290                scsi_dh_handler_attach(sdev, scsi_dh);
 291
 292        put_device(dev);
 293
 294        return 0;
 295}
 296
 297/*
 298 * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
 299 */
 300static int scsi_dh_notifier_remove(struct device *dev, void *data)
 301{
 302        struct scsi_device_handler *scsi_dh = data;
 303        struct scsi_device *sdev;
 304
 305        if (!scsi_is_sdev_device(dev))
 306                return 0;
 307
 308        if (!get_device(dev))
 309                return 0;
 310
 311        sdev = to_scsi_device(dev);
 312
 313        scsi_dh_handler_detach(sdev, scsi_dh);
 314
 315        put_device(dev);
 316
 317        return 0;
 318}
 319
 320/*
 321 * scsi_register_device_handler - register a device handler personality
 322 *      module.
 323 * @scsi_dh - device handler to be registered.
 324 *
 325 * Returns 0 on success, -EBUSY if handler already registered.
 326 */
 327int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
 328{
 329
 330        if (get_device_handler(scsi_dh->name))
 331                return -EBUSY;
 332
 333        spin_lock(&list_lock);
 334        list_add(&scsi_dh->list, &scsi_dh_list);
 335        spin_unlock(&list_lock);
 336
 337        bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
 338        printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
 339
 340        return SCSI_DH_OK;
 341}
 342EXPORT_SYMBOL_GPL(scsi_register_device_handler);
 343
 344/*
 345 * scsi_unregister_device_handler - register a device handler personality
 346 *      module.
 347 * @scsi_dh - device handler to be unregistered.
 348 *
 349 * Returns 0 on success, -ENODEV if handler not registered.
 350 */
 351int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
 352{
 353
 354        if (!get_device_handler(scsi_dh->name))
 355                return -ENODEV;
 356
 357        bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
 358                         scsi_dh_notifier_remove);
 359
 360        spin_lock(&list_lock);
 361        list_del(&scsi_dh->list);
 362        spin_unlock(&list_lock);
 363        printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
 364
 365        return SCSI_DH_OK;
 366}
 367EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
 368
 369/*
 370 * scsi_dh_activate - activate the path associated with the scsi_device
 371 *      corresponding to the given request queue.
 372 *     Returns immediately without waiting for activation to be completed.
 373 * @q    - Request queue that is associated with the scsi_device to be
 374 *         activated.
 375 * @fn   - Function to be called upon completion of the activation.
 376 *         Function fn is called with data (below) and the error code.
 377 *         Function fn may be called from the same calling context. So,
 378 *         do not hold the lock in the caller which may be needed in fn.
 379 * @data - data passed to the function fn upon completion.
 380 *
 381 */
 382int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
 383{
 384        int err = 0;
 385        unsigned long flags;
 386        struct scsi_device *sdev;
 387        struct scsi_device_handler *scsi_dh = NULL;
 388        struct device *dev = NULL;
 389
 390        spin_lock_irqsave(q->queue_lock, flags);
 391        sdev = q->queuedata;
 392        if (!sdev) {
 393                spin_unlock_irqrestore(q->queue_lock, flags);
 394                err = SCSI_DH_NOSYS;
 395                if (fn)
 396                        fn(data, err);
 397                return err;
 398        }
 399
 400        if (sdev->scsi_dh_data)
 401                scsi_dh = sdev->scsi_dh_data->scsi_dh;
 402        dev = get_device(&sdev->sdev_gendev);
 403        if (!scsi_dh || !dev ||
 404            sdev->sdev_state == SDEV_CANCEL ||
 405            sdev->sdev_state == SDEV_DEL)
 406                err = SCSI_DH_NOSYS;
 407        if (sdev->sdev_state == SDEV_OFFLINE)
 408                err = SCSI_DH_DEV_OFFLINED;
 409        spin_unlock_irqrestore(q->queue_lock, flags);
 410
 411        if (err) {
 412                if (fn)
 413                        fn(data, err);
 414                goto out;
 415        }
 416
 417        if (scsi_dh->activate)
 418                err = scsi_dh->activate(sdev, fn, data);
 419out:
 420        put_device(dev);
 421        return err;
 422}
 423EXPORT_SYMBOL_GPL(scsi_dh_activate);
 424
 425/*
 426 * scsi_dh_set_params - set the parameters for the device as per the
 427 *      string specified in params.
 428 * @q - Request queue that is associated with the scsi_device for
 429 *      which the parameters to be set.
 430 * @params - parameters in the following format
 431 *      "no_of_params\0param1\0param2\0param3\0...\0"
 432 *      for example, string for 2 parameters with value 10 and 21
 433 *      is specified as "2\010\021\0".
 434 */
 435int scsi_dh_set_params(struct request_queue *q, const char *params)
 436{
 437        int err = -SCSI_DH_NOSYS;
 438        unsigned long flags;
 439        struct scsi_device *sdev;
 440        struct scsi_device_handler *scsi_dh = NULL;
 441
 442        spin_lock_irqsave(q->queue_lock, flags);
 443        sdev = q->queuedata;
 444        if (sdev && sdev->scsi_dh_data)
 445                scsi_dh = sdev->scsi_dh_data->scsi_dh;
 446        if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev))
 447                err = 0;
 448        spin_unlock_irqrestore(q->queue_lock, flags);
 449
 450        if (err)
 451                return err;
 452        err = scsi_dh->set_params(sdev, params);
 453        put_device(&sdev->sdev_gendev);
 454        return err;
 455}
 456EXPORT_SYMBOL_GPL(scsi_dh_set_params);
 457
 458/*
 459 * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
 460 *      the given name. FALSE(0) otherwise.
 461 * @name - name of the device handler.
 462 */
 463int scsi_dh_handler_exist(const char *name)
 464{
 465        return (get_device_handler(name) != NULL);
 466}
 467EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
 468
 469/*
 470 * scsi_dh_attach - Attach device handler
 471 * @q - Request queue that is associated with the scsi_device
 472 *      the handler should be attached to
 473 * @name - name of the handler to attach
 474 */
 475int scsi_dh_attach(struct request_queue *q, const char *name)
 476{
 477        unsigned long flags;
 478        struct scsi_device *sdev;
 479        struct scsi_device_handler *scsi_dh;
 480        int err = 0;
 481
 482        scsi_dh = get_device_handler(name);
 483        if (!scsi_dh)
 484                return -EINVAL;
 485
 486        spin_lock_irqsave(q->queue_lock, flags);
 487        sdev = q->queuedata;
 488        if (!sdev || !get_device(&sdev->sdev_gendev))
 489                err = -ENODEV;
 490        spin_unlock_irqrestore(q->queue_lock, flags);
 491
 492        if (!err) {
 493                err = scsi_dh_handler_attach(sdev, scsi_dh);
 494                put_device(&sdev->sdev_gendev);
 495        }
 496        return err;
 497}
 498EXPORT_SYMBOL_GPL(scsi_dh_attach);
 499
 500/*
 501 * scsi_dh_detach - Detach device handler
 502 * @q - Request queue that is associated with the scsi_device
 503 *      the handler should be detached from
 504 *
 505 * This function will detach the device handler only
 506 * if the sdev is not part of the internal list, ie
 507 * if it has been attached manually.
 508 */
 509void scsi_dh_detach(struct request_queue *q)
 510{
 511        unsigned long flags;
 512        struct scsi_device *sdev;
 513        struct scsi_device_handler *scsi_dh = NULL;
 514
 515        spin_lock_irqsave(q->queue_lock, flags);
 516        sdev = q->queuedata;
 517        if (!sdev || !get_device(&sdev->sdev_gendev))
 518                sdev = NULL;
 519        spin_unlock_irqrestore(q->queue_lock, flags);
 520
 521        if (!sdev)
 522                return;
 523
 524        if (sdev->scsi_dh_data) {
 525                scsi_dh = sdev->scsi_dh_data->scsi_dh;
 526                scsi_dh_handler_detach(sdev, scsi_dh);
 527        }
 528        put_device(&sdev->sdev_gendev);
 529}
 530EXPORT_SYMBOL_GPL(scsi_dh_detach);
 531
 532/*
 533 * scsi_dh_attached_handler_name - Get attached device handler's name
 534 * @q - Request queue that is associated with the scsi_device
 535 *      that may have a device handler attached
 536 * @gfp - the GFP mask used in the kmalloc() call when allocating memory
 537 *
 538 * Returns name of attached handler, NULL if no handler is attached.
 539 * Caller must take care to free the returned string.
 540 */
 541const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
 542{
 543        unsigned long flags;
 544        struct scsi_device *sdev;
 545        const char *handler_name = NULL;
 546
 547        spin_lock_irqsave(q->queue_lock, flags);
 548        sdev = q->queuedata;
 549        if (!sdev || !get_device(&sdev->sdev_gendev))
 550                sdev = NULL;
 551        spin_unlock_irqrestore(q->queue_lock, flags);
 552
 553        if (!sdev)
 554                return NULL;
 555
 556        if (sdev->scsi_dh_data)
 557                handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp);
 558
 559        put_device(&sdev->sdev_gendev);
 560        return handler_name;
 561}
 562EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name);
 563
 564static struct notifier_block scsi_dh_nb = {
 565        .notifier_call = scsi_dh_notifier
 566};
 567
 568static int __init scsi_dh_init(void)
 569{
 570        int r;
 571
 572        r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
 573
 574        if (!r)
 575                bus_for_each_dev(&scsi_bus_type, NULL, NULL,
 576                                 scsi_dh_sysfs_attr_add);
 577
 578        return r;
 579}
 580
 581static void __exit scsi_dh_exit(void)
 582{
 583        bus_for_each_dev(&scsi_bus_type, NULL, NULL,
 584                         scsi_dh_sysfs_attr_remove);
 585        bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
 586}
 587
 588module_init(scsi_dh_init);
 589module_exit(scsi_dh_exit);
 590
 591MODULE_DESCRIPTION("SCSI device handler");
 592MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
 593MODULE_LICENSE("GPL");
 594