Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
0004  * upgraded.
0005  *
0006  * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
0007  * Copyright (C) 2006 Mike Christie
0008  * Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
0009  */
0010 
0011 #include <linux/slab.h>
0012 #include <linux/module.h>
0013 #include <scsi/scsi.h>
0014 #include <scsi/scsi_dbg.h>
0015 #include <scsi/scsi_eh.h>
0016 #include <scsi/scsi_dh.h>
0017 
0018 #define HP_SW_NAME          "hp_sw"
0019 
0020 #define HP_SW_TIMEOUT           (60 * HZ)
0021 #define HP_SW_RETRIES           3
0022 
0023 #define HP_SW_PATH_UNINITIALIZED    -1
0024 #define HP_SW_PATH_ACTIVE       0
0025 #define HP_SW_PATH_PASSIVE      1
0026 
0027 struct hp_sw_dh_data {
0028     int path_state;
0029     int retries;
0030     int retry_cnt;
0031     struct scsi_device *sdev;
0032 };
0033 
0034 static int hp_sw_start_stop(struct hp_sw_dh_data *);
0035 
0036 /*
0037  * tur_done - Handle TEST UNIT READY return status
0038  * @sdev: sdev the command has been sent to
0039  * @errors: blk error code
0040  *
0041  * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
0042  */
0043 static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h,
0044             struct scsi_sense_hdr *sshdr)
0045 {
0046     int ret = SCSI_DH_IO;
0047 
0048     switch (sshdr->sense_key) {
0049     case UNIT_ATTENTION:
0050         ret = SCSI_DH_IMM_RETRY;
0051         break;
0052     case NOT_READY:
0053         if (sshdr->asc == 0x04 && sshdr->ascq == 2) {
0054             /*
0055              * LUN not ready - Initialization command required
0056              *
0057              * This is the passive path
0058              */
0059             h->path_state = HP_SW_PATH_PASSIVE;
0060             ret = SCSI_DH_OK;
0061             break;
0062         }
0063         fallthrough;
0064     default:
0065         sdev_printk(KERN_WARNING, sdev,
0066                "%s: sending tur failed, sense %x/%x/%x\n",
0067                HP_SW_NAME, sshdr->sense_key, sshdr->asc,
0068                sshdr->ascq);
0069         break;
0070     }
0071     return ret;
0072 }
0073 
0074 /*
0075  * hp_sw_tur - Send TEST UNIT READY
0076  * @sdev: sdev command should be sent to
0077  *
0078  * Use the TEST UNIT READY command to determine
0079  * the path state.
0080  */
0081 static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
0082 {
0083     unsigned char cmd[6] = { TEST_UNIT_READY };
0084     struct scsi_sense_hdr sshdr;
0085     int ret = SCSI_DH_OK, res;
0086     blk_opf_t req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
0087         REQ_FAILFAST_DRIVER;
0088 
0089 retry:
0090     res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
0091             HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL);
0092     if (res) {
0093         if (scsi_sense_valid(&sshdr))
0094             ret = tur_done(sdev, h, &sshdr);
0095         else {
0096             sdev_printk(KERN_WARNING, sdev,
0097                     "%s: sending tur failed with %x\n",
0098                     HP_SW_NAME, res);
0099             ret = SCSI_DH_IO;
0100         }
0101     } else {
0102         h->path_state = HP_SW_PATH_ACTIVE;
0103         ret = SCSI_DH_OK;
0104     }
0105     if (ret == SCSI_DH_IMM_RETRY)
0106         goto retry;
0107 
0108     return ret;
0109 }
0110 
0111 /*
0112  * hp_sw_start_stop - Send START STOP UNIT command
0113  * @sdev: sdev command should be sent to
0114  *
0115  * Sending START STOP UNIT activates the SP.
0116  */
0117 static int hp_sw_start_stop(struct hp_sw_dh_data *h)
0118 {
0119     unsigned char cmd[6] = { START_STOP, 0, 0, 0, 1, 0 };
0120     struct scsi_sense_hdr sshdr;
0121     struct scsi_device *sdev = h->sdev;
0122     int res, rc = SCSI_DH_OK;
0123     int retry_cnt = HP_SW_RETRIES;
0124     blk_opf_t req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
0125         REQ_FAILFAST_DRIVER;
0126 
0127 retry:
0128     res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
0129             HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL);
0130     if (res) {
0131         if (!scsi_sense_valid(&sshdr)) {
0132             sdev_printk(KERN_WARNING, sdev,
0133                     "%s: sending start_stop_unit failed, "
0134                     "no sense available\n", HP_SW_NAME);
0135             return SCSI_DH_IO;
0136         }
0137         switch (sshdr.sense_key) {
0138         case NOT_READY:
0139             if (sshdr.asc == 0x04 && sshdr.ascq == 3) {
0140                 /*
0141                  * LUN not ready - manual intervention required
0142                  *
0143                  * Switch-over in progress, retry.
0144                  */
0145                 if (--retry_cnt)
0146                     goto retry;
0147                 rc = SCSI_DH_RETRY;
0148                 break;
0149             }
0150             fallthrough;
0151         default:
0152             sdev_printk(KERN_WARNING, sdev,
0153                     "%s: sending start_stop_unit failed, "
0154                     "sense %x/%x/%x\n", HP_SW_NAME,
0155                     sshdr.sense_key, sshdr.asc, sshdr.ascq);
0156             rc = SCSI_DH_IO;
0157         }
0158     }
0159     return rc;
0160 }
0161 
0162 static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
0163 {
0164     struct hp_sw_dh_data *h = sdev->handler_data;
0165 
0166     if (h->path_state != HP_SW_PATH_ACTIVE) {
0167         req->rq_flags |= RQF_QUIET;
0168         return BLK_STS_IOERR;
0169     }
0170 
0171     return BLK_STS_OK;
0172 }
0173 
0174 /*
0175  * hp_sw_activate - Activate a path
0176  * @sdev: sdev on the path to be activated
0177  *
0178  * The HP Active/Passive firmware is pretty simple;
0179  * the passive path reports NOT READY with sense codes
0180  * 0x04/0x02; a START STOP UNIT command will then
0181  * activate the passive path (and deactivate the
0182  * previously active one).
0183  */
0184 static int hp_sw_activate(struct scsi_device *sdev,
0185                 activate_complete fn, void *data)
0186 {
0187     int ret = SCSI_DH_OK;
0188     struct hp_sw_dh_data *h = sdev->handler_data;
0189 
0190     ret = hp_sw_tur(sdev, h);
0191 
0192     if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE)
0193         ret = hp_sw_start_stop(h);
0194 
0195     if (fn)
0196         fn(data, ret);
0197     return 0;
0198 }
0199 
0200 static int hp_sw_bus_attach(struct scsi_device *sdev)
0201 {
0202     struct hp_sw_dh_data *h;
0203     int ret;
0204 
0205     h = kzalloc(sizeof(*h), GFP_KERNEL);
0206     if (!h)
0207         return SCSI_DH_NOMEM;
0208     h->path_state = HP_SW_PATH_UNINITIALIZED;
0209     h->retries = HP_SW_RETRIES;
0210     h->sdev = sdev;
0211 
0212     ret = hp_sw_tur(sdev, h);
0213     if (ret != SCSI_DH_OK)
0214         goto failed;
0215     if (h->path_state == HP_SW_PATH_UNINITIALIZED) {
0216         ret = SCSI_DH_NOSYS;
0217         goto failed;
0218     }
0219 
0220     sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
0221             HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
0222             "active":"passive");
0223 
0224     sdev->handler_data = h;
0225     return SCSI_DH_OK;
0226 failed:
0227     kfree(h);
0228     return ret;
0229 }
0230 
0231 static void hp_sw_bus_detach( struct scsi_device *sdev )
0232 {
0233     kfree(sdev->handler_data);
0234     sdev->handler_data = NULL;
0235 }
0236 
0237 static struct scsi_device_handler hp_sw_dh = {
0238     .name       = HP_SW_NAME,
0239     .module     = THIS_MODULE,
0240     .attach     = hp_sw_bus_attach,
0241     .detach     = hp_sw_bus_detach,
0242     .activate   = hp_sw_activate,
0243     .prep_fn    = hp_sw_prep_fn,
0244 };
0245 
0246 static int __init hp_sw_init(void)
0247 {
0248     return scsi_register_device_handler(&hp_sw_dh);
0249 }
0250 
0251 static void __exit hp_sw_exit(void)
0252 {
0253     scsi_unregister_device_handler(&hp_sw_dh);
0254 }
0255 
0256 module_init(hp_sw_init);
0257 module_exit(hp_sw_exit);
0258 
0259 MODULE_DESCRIPTION("HP Active/Passive driver");
0260 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
0261 MODULE_LICENSE("GPL");