162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCSI device handler infrastructure. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corporation, 2007 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Chandra Seetharaman <sekharan@us.ibm.com> 862306a36Sopenharmony_ci * Mike Anderson <andmike@linux.vnet.ibm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <scsi/scsi_dh.h> 1462306a36Sopenharmony_ci#include "scsi_priv.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(list_lock); 1762306a36Sopenharmony_cistatic LIST_HEAD(scsi_dh_list); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct scsi_dh_blist { 2062306a36Sopenharmony_ci const char *vendor; 2162306a36Sopenharmony_ci const char *model; 2262306a36Sopenharmony_ci const char *driver; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic const struct scsi_dh_blist scsi_dh_blist[] = { 2662306a36Sopenharmony_ci {"DGC", "RAID", "emc" }, 2762306a36Sopenharmony_ci {"DGC", "DISK", "emc" }, 2862306a36Sopenharmony_ci {"DGC", "VRAID", "emc" }, 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci {"COMPAQ", "MSA1000 VOLUME", "hp_sw" }, 3162306a36Sopenharmony_ci {"COMPAQ", "HSV110", "hp_sw" }, 3262306a36Sopenharmony_ci {"HP", "HSV100", "hp_sw"}, 3362306a36Sopenharmony_ci {"DEC", "HSG80", "hp_sw"}, 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci {"IBM", "1722", "rdac", }, 3662306a36Sopenharmony_ci {"IBM", "1724", "rdac", }, 3762306a36Sopenharmony_ci {"IBM", "1726", "rdac", }, 3862306a36Sopenharmony_ci {"IBM", "1742", "rdac", }, 3962306a36Sopenharmony_ci {"IBM", "1745", "rdac", }, 4062306a36Sopenharmony_ci {"IBM", "1746", "rdac", }, 4162306a36Sopenharmony_ci {"IBM", "1813", "rdac", }, 4262306a36Sopenharmony_ci {"IBM", "1814", "rdac", }, 4362306a36Sopenharmony_ci {"IBM", "1815", "rdac", }, 4462306a36Sopenharmony_ci {"IBM", "1818", "rdac", }, 4562306a36Sopenharmony_ci {"IBM", "3526", "rdac", }, 4662306a36Sopenharmony_ci {"IBM", "3542", "rdac", }, 4762306a36Sopenharmony_ci {"IBM", "3552", "rdac", }, 4862306a36Sopenharmony_ci {"SGI", "TP9300", "rdac", }, 4962306a36Sopenharmony_ci {"SGI", "TP9400", "rdac", }, 5062306a36Sopenharmony_ci {"SGI", "TP9500", "rdac", }, 5162306a36Sopenharmony_ci {"SGI", "TP9700", "rdac", }, 5262306a36Sopenharmony_ci {"SGI", "IS", "rdac", }, 5362306a36Sopenharmony_ci {"STK", "OPENstorage", "rdac", }, 5462306a36Sopenharmony_ci {"STK", "FLEXLINE 380", "rdac", }, 5562306a36Sopenharmony_ci {"STK", "BladeCtlr", "rdac", }, 5662306a36Sopenharmony_ci {"SUN", "CSM", "rdac", }, 5762306a36Sopenharmony_ci {"SUN", "LCSM100", "rdac", }, 5862306a36Sopenharmony_ci {"SUN", "STK6580_6780", "rdac", }, 5962306a36Sopenharmony_ci {"SUN", "SUN_6180", "rdac", }, 6062306a36Sopenharmony_ci {"SUN", "ArrayStorage", "rdac", }, 6162306a36Sopenharmony_ci {"DELL", "MD3", "rdac", }, 6262306a36Sopenharmony_ci {"NETAPP", "INF-01-00", "rdac", }, 6362306a36Sopenharmony_ci {"LSI", "INF-01-00", "rdac", }, 6462306a36Sopenharmony_ci {"ENGENIO", "INF-01-00", "rdac", }, 6562306a36Sopenharmony_ci {"LENOVO", "DE_Series", "rdac", }, 6662306a36Sopenharmony_ci {"FUJITSU", "ETERNUS_AHB", "rdac", }, 6762306a36Sopenharmony_ci {NULL, NULL, NULL }, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const char * 7162306a36Sopenharmony_ciscsi_dh_find_driver(struct scsi_device *sdev) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci const struct scsi_dh_blist *b; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (scsi_device_tpgs(sdev)) 7662306a36Sopenharmony_ci return "alua"; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (b = scsi_dh_blist; b->vendor; b++) { 7962306a36Sopenharmony_ci if (!strncmp(sdev->vendor, b->vendor, strlen(b->vendor)) && 8062306a36Sopenharmony_ci !strncmp(sdev->model, b->model, strlen(b->model))) { 8162306a36Sopenharmony_ci return b->driver; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci return NULL; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct scsi_device_handler *__scsi_dh_lookup(const char *name) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct scsi_device_handler *tmp, *found = NULL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci spin_lock(&list_lock); 9362306a36Sopenharmony_ci list_for_each_entry(tmp, &scsi_dh_list, list) { 9462306a36Sopenharmony_ci if (!strncmp(tmp->name, name, strlen(tmp->name))) { 9562306a36Sopenharmony_ci found = tmp; 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci spin_unlock(&list_lock); 10062306a36Sopenharmony_ci return found; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic struct scsi_device_handler *scsi_dh_lookup(const char *name) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct scsi_device_handler *dh; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!name || strlen(name) == 0) 10862306a36Sopenharmony_ci return NULL; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci dh = __scsi_dh_lookup(name); 11162306a36Sopenharmony_ci if (!dh) { 11262306a36Sopenharmony_ci request_module("scsi_dh_%s", name); 11362306a36Sopenharmony_ci dh = __scsi_dh_lookup(name); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return dh; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * scsi_dh_handler_attach - Attach a device handler to a device 12162306a36Sopenharmony_ci * @sdev - SCSI device the device handler should attach to 12262306a36Sopenharmony_ci * @scsi_dh - The device handler to attach 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic int scsi_dh_handler_attach(struct scsi_device *sdev, 12562306a36Sopenharmony_ci struct scsi_device_handler *scsi_dh) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int error, ret = 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!try_module_get(scsi_dh->module)) 13062306a36Sopenharmony_ci return -EINVAL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci error = scsi_dh->attach(sdev); 13362306a36Sopenharmony_ci if (error != SCSI_DH_OK) { 13462306a36Sopenharmony_ci switch (error) { 13562306a36Sopenharmony_ci case SCSI_DH_NOMEM: 13662306a36Sopenharmony_ci ret = -ENOMEM; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case SCSI_DH_RES_TEMP_UNAVAIL: 13962306a36Sopenharmony_ci ret = -EAGAIN; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci case SCSI_DH_DEV_UNSUPP: 14262306a36Sopenharmony_ci case SCSI_DH_NOSYS: 14362306a36Sopenharmony_ci ret = -ENODEV; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci default: 14662306a36Sopenharmony_ci ret = -EINVAL; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci if (ret != -ENODEV) 15062306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%d)\n", 15162306a36Sopenharmony_ci scsi_dh->name, error); 15262306a36Sopenharmony_ci module_put(scsi_dh->module); 15362306a36Sopenharmony_ci } else 15462306a36Sopenharmony_ci sdev->handler = scsi_dh; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * scsi_dh_handler_detach - Detach a device handler from a device 16162306a36Sopenharmony_ci * @sdev - SCSI device the device handler should be detached from 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic void scsi_dh_handler_detach(struct scsi_device *sdev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci sdev->handler->detach(sdev); 16662306a36Sopenharmony_ci sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", sdev->handler->name); 16762306a36Sopenharmony_ci module_put(sdev->handler->module); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_civoid scsi_dh_add_device(struct scsi_device *sdev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct scsi_device_handler *devinfo = NULL; 17362306a36Sopenharmony_ci const char *drv; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci drv = scsi_dh_find_driver(sdev); 17662306a36Sopenharmony_ci if (drv) 17762306a36Sopenharmony_ci devinfo = __scsi_dh_lookup(drv); 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * device_handler is optional, so ignore errors 18062306a36Sopenharmony_ci * from scsi_dh_handler_attach() 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (devinfo) 18362306a36Sopenharmony_ci (void)scsi_dh_handler_attach(sdev, devinfo); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_civoid scsi_dh_release_device(struct scsi_device *sdev) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (sdev->handler) 18962306a36Sopenharmony_ci scsi_dh_handler_detach(sdev); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * scsi_register_device_handler - register a device handler personality 19462306a36Sopenharmony_ci * module. 19562306a36Sopenharmony_ci * @scsi_dh - device handler to be registered. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * Returns 0 on success, -EBUSY if handler already registered. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ciint scsi_register_device_handler(struct scsi_device_handler *scsi_dh) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci if (__scsi_dh_lookup(scsi_dh->name)) 20262306a36Sopenharmony_ci return -EBUSY; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!scsi_dh->attach || !scsi_dh->detach) 20562306a36Sopenharmony_ci return -EINVAL; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci spin_lock(&list_lock); 20862306a36Sopenharmony_ci list_add(&scsi_dh->list, &scsi_dh_list); 20962306a36Sopenharmony_ci spin_unlock(&list_lock); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return SCSI_DH_OK; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_register_device_handler); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* 21862306a36Sopenharmony_ci * scsi_unregister_device_handler - register a device handler personality 21962306a36Sopenharmony_ci * module. 22062306a36Sopenharmony_ci * @scsi_dh - device handler to be unregistered. 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * Returns 0 on success, -ENODEV if handler not registered. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ciint scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci if (!__scsi_dh_lookup(scsi_dh->name)) 22762306a36Sopenharmony_ci return -ENODEV; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci spin_lock(&list_lock); 23062306a36Sopenharmony_ci list_del(&scsi_dh->list); 23162306a36Sopenharmony_ci spin_unlock(&list_lock); 23262306a36Sopenharmony_ci printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return SCSI_DH_OK; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_unregister_device_handler); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * scsi_dh_activate - activate the path associated with the scsi_device 24062306a36Sopenharmony_ci * corresponding to the given request queue. 24162306a36Sopenharmony_ci * Returns immediately without waiting for activation to be completed. 24262306a36Sopenharmony_ci * @q - Request queue that is associated with the scsi_device to be 24362306a36Sopenharmony_ci * activated. 24462306a36Sopenharmony_ci * @fn - Function to be called upon completion of the activation. 24562306a36Sopenharmony_ci * Function fn is called with data (below) and the error code. 24662306a36Sopenharmony_ci * Function fn may be called from the same calling context. So, 24762306a36Sopenharmony_ci * do not hold the lock in the caller which may be needed in fn. 24862306a36Sopenharmony_ci * @data - data passed to the function fn upon completion. 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ciint scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct scsi_device *sdev; 25462306a36Sopenharmony_ci int err = SCSI_DH_NOSYS; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci sdev = scsi_device_from_queue(q); 25762306a36Sopenharmony_ci if (!sdev) { 25862306a36Sopenharmony_ci if (fn) 25962306a36Sopenharmony_ci fn(data, err); 26062306a36Sopenharmony_ci return err; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!sdev->handler) 26462306a36Sopenharmony_ci goto out_fn; 26562306a36Sopenharmony_ci err = SCSI_DH_NOTCONN; 26662306a36Sopenharmony_ci if (sdev->sdev_state == SDEV_CANCEL || 26762306a36Sopenharmony_ci sdev->sdev_state == SDEV_DEL) 26862306a36Sopenharmony_ci goto out_fn; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci err = SCSI_DH_DEV_OFFLINED; 27162306a36Sopenharmony_ci if (sdev->sdev_state == SDEV_OFFLINE) 27262306a36Sopenharmony_ci goto out_fn; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (sdev->handler->activate) 27562306a36Sopenharmony_ci err = sdev->handler->activate(sdev, fn, data); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciout_put_device: 27862306a36Sopenharmony_ci put_device(&sdev->sdev_gendev); 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciout_fn: 28262306a36Sopenharmony_ci if (fn) 28362306a36Sopenharmony_ci fn(data, err); 28462306a36Sopenharmony_ci goto out_put_device; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_dh_activate); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * scsi_dh_set_params - set the parameters for the device as per the 29062306a36Sopenharmony_ci * string specified in params. 29162306a36Sopenharmony_ci * @q - Request queue that is associated with the scsi_device for 29262306a36Sopenharmony_ci * which the parameters to be set. 29362306a36Sopenharmony_ci * @params - parameters in the following format 29462306a36Sopenharmony_ci * "no_of_params\0param1\0param2\0param3\0...\0" 29562306a36Sopenharmony_ci * for example, string for 2 parameters with value 10 and 21 29662306a36Sopenharmony_ci * is specified as "2\010\021\0". 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ciint scsi_dh_set_params(struct request_queue *q, const char *params) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct scsi_device *sdev; 30162306a36Sopenharmony_ci int err = -SCSI_DH_NOSYS; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci sdev = scsi_device_from_queue(q); 30462306a36Sopenharmony_ci if (!sdev) 30562306a36Sopenharmony_ci return err; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (sdev->handler && sdev->handler->set_params) 30862306a36Sopenharmony_ci err = sdev->handler->set_params(sdev, params); 30962306a36Sopenharmony_ci put_device(&sdev->sdev_gendev); 31062306a36Sopenharmony_ci return err; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_dh_set_params); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* 31562306a36Sopenharmony_ci * scsi_dh_attach - Attach device handler 31662306a36Sopenharmony_ci * @q - Request queue that is associated with the scsi_device 31762306a36Sopenharmony_ci * the handler should be attached to 31862306a36Sopenharmony_ci * @name - name of the handler to attach 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ciint scsi_dh_attach(struct request_queue *q, const char *name) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct scsi_device *sdev; 32362306a36Sopenharmony_ci struct scsi_device_handler *scsi_dh; 32462306a36Sopenharmony_ci int err = 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci sdev = scsi_device_from_queue(q); 32762306a36Sopenharmony_ci if (!sdev) 32862306a36Sopenharmony_ci return -ENODEV; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci scsi_dh = scsi_dh_lookup(name); 33162306a36Sopenharmony_ci if (!scsi_dh) { 33262306a36Sopenharmony_ci err = -EINVAL; 33362306a36Sopenharmony_ci goto out_put_device; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (sdev->handler) { 33762306a36Sopenharmony_ci if (sdev->handler != scsi_dh) 33862306a36Sopenharmony_ci err = -EBUSY; 33962306a36Sopenharmony_ci goto out_put_device; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci err = scsi_dh_handler_attach(sdev, scsi_dh); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciout_put_device: 34562306a36Sopenharmony_ci put_device(&sdev->sdev_gendev); 34662306a36Sopenharmony_ci return err; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_dh_attach); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * scsi_dh_attached_handler_name - Get attached device handler's name 35262306a36Sopenharmony_ci * @q - Request queue that is associated with the scsi_device 35362306a36Sopenharmony_ci * that may have a device handler attached 35462306a36Sopenharmony_ci * @gfp - the GFP mask used in the kmalloc() call when allocating memory 35562306a36Sopenharmony_ci * 35662306a36Sopenharmony_ci * Returns name of attached handler, NULL if no handler is attached. 35762306a36Sopenharmony_ci * Caller must take care to free the returned string. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ciconst char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct scsi_device *sdev; 36262306a36Sopenharmony_ci const char *handler_name = NULL; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci sdev = scsi_device_from_queue(q); 36562306a36Sopenharmony_ci if (!sdev) 36662306a36Sopenharmony_ci return NULL; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (sdev->handler) 36962306a36Sopenharmony_ci handler_name = kstrdup(sdev->handler->name, gfp); 37062306a36Sopenharmony_ci put_device(&sdev->sdev_gendev); 37162306a36Sopenharmony_ci return handler_name; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); 374