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