root/drivers/scsi/device_handler/scsi_dh_hp_sw.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tur_done
  2. hp_sw_tur
  3. hp_sw_start_stop
  4. hp_sw_prep_fn
  5. hp_sw_activate
  6. hp_sw_bus_attach
  7. hp_sw_bus_detach
  8. hp_sw_init
  9. hp_sw_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
   4  * upgraded.
   5  *
   6  * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
   7  * Copyright (C) 2006 Mike Christie
   8  * Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
   9  */
  10 
  11 #include <linux/slab.h>
  12 #include <linux/module.h>
  13 #include <scsi/scsi.h>
  14 #include <scsi/scsi_dbg.h>
  15 #include <scsi/scsi_eh.h>
  16 #include <scsi/scsi_dh.h>
  17 
  18 #define HP_SW_NAME                      "hp_sw"
  19 
  20 #define HP_SW_TIMEOUT                   (60 * HZ)
  21 #define HP_SW_RETRIES                   3
  22 
  23 #define HP_SW_PATH_UNINITIALIZED        -1
  24 #define HP_SW_PATH_ACTIVE               0
  25 #define HP_SW_PATH_PASSIVE              1
  26 
  27 struct hp_sw_dh_data {
  28         int path_state;
  29         int retries;
  30         int retry_cnt;
  31         struct scsi_device *sdev;
  32 };
  33 
  34 static int hp_sw_start_stop(struct hp_sw_dh_data *);
  35 
  36 /*
  37  * tur_done - Handle TEST UNIT READY return status
  38  * @sdev: sdev the command has been sent to
  39  * @errors: blk error code
  40  *
  41  * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
  42  */
  43 static int tur_done(struct scsi_device *sdev, struct hp_sw_dh_data *h,
  44                     struct scsi_sense_hdr *sshdr)
  45 {
  46         int ret = SCSI_DH_IO;
  47 
  48         switch (sshdr->sense_key) {
  49         case UNIT_ATTENTION:
  50                 ret = SCSI_DH_IMM_RETRY;
  51                 break;
  52         case NOT_READY:
  53                 if (sshdr->asc == 0x04 && sshdr->ascq == 2) {
  54                         /*
  55                          * LUN not ready - Initialization command required
  56                          *
  57                          * This is the passive path
  58                          */
  59                         h->path_state = HP_SW_PATH_PASSIVE;
  60                         ret = SCSI_DH_OK;
  61                         break;
  62                 }
  63                 /* Fallthrough */
  64         default:
  65                 sdev_printk(KERN_WARNING, sdev,
  66                            "%s: sending tur failed, sense %x/%x/%x\n",
  67                            HP_SW_NAME, sshdr->sense_key, sshdr->asc,
  68                            sshdr->ascq);
  69                 break;
  70         }
  71         return ret;
  72 }
  73 
  74 /*
  75  * hp_sw_tur - Send TEST UNIT READY
  76  * @sdev: sdev command should be sent to
  77  *
  78  * Use the TEST UNIT READY command to determine
  79  * the path state.
  80  */
  81 static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
  82 {
  83         unsigned char cmd[6] = { TEST_UNIT_READY };
  84         struct scsi_sense_hdr sshdr;
  85         int ret = SCSI_DH_OK, res;
  86         u64 req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
  87                 REQ_FAILFAST_DRIVER;
  88 
  89 retry:
  90         res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
  91                         HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL);
  92         if (res) {
  93                 if (scsi_sense_valid(&sshdr))
  94                         ret = tur_done(sdev, h, &sshdr);
  95                 else {
  96                         sdev_printk(KERN_WARNING, sdev,
  97                                     "%s: sending tur failed with %x\n",
  98                                     HP_SW_NAME, res);
  99                         ret = SCSI_DH_IO;
 100                 }
 101         } else {
 102                 h->path_state = HP_SW_PATH_ACTIVE;
 103                 ret = SCSI_DH_OK;
 104         }
 105         if (ret == SCSI_DH_IMM_RETRY)
 106                 goto retry;
 107 
 108         return ret;
 109 }
 110 
 111 /*
 112  * hp_sw_start_stop - Send START STOP UNIT command
 113  * @sdev: sdev command should be sent to
 114  *
 115  * Sending START STOP UNIT activates the SP.
 116  */
 117 static int hp_sw_start_stop(struct hp_sw_dh_data *h)
 118 {
 119         unsigned char cmd[6] = { START_STOP, 0, 0, 0, 1, 0 };
 120         struct scsi_sense_hdr sshdr;
 121         struct scsi_device *sdev = h->sdev;
 122         int res, rc = SCSI_DH_OK;
 123         int retry_cnt = HP_SW_RETRIES;
 124         u64 req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
 125                 REQ_FAILFAST_DRIVER;
 126 
 127 retry:
 128         res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
 129                         HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL);
 130         if (res) {
 131                 if (!scsi_sense_valid(&sshdr)) {
 132                         sdev_printk(KERN_WARNING, sdev,
 133                                     "%s: sending start_stop_unit failed, "
 134                                     "no sense available\n", HP_SW_NAME);
 135                         return SCSI_DH_IO;
 136                 }
 137                 switch (sshdr.sense_key) {
 138                 case NOT_READY:
 139                         if (sshdr.asc == 0x04 && sshdr.ascq == 3) {
 140                                 /*
 141                                  * LUN not ready - manual intervention required
 142                                  *
 143                                  * Switch-over in progress, retry.
 144                                  */
 145                                 if (--retry_cnt)
 146                                         goto retry;
 147                                 rc = SCSI_DH_RETRY;
 148                                 break;
 149                         }
 150                         /* fall through */
 151                 default:
 152                         sdev_printk(KERN_WARNING, sdev,
 153                                     "%s: sending start_stop_unit failed, "
 154                                     "sense %x/%x/%x\n", HP_SW_NAME,
 155                                     sshdr.sense_key, sshdr.asc, sshdr.ascq);
 156                         rc = SCSI_DH_IO;
 157                 }
 158         }
 159         return rc;
 160 }
 161 
 162 static blk_status_t hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
 163 {
 164         struct hp_sw_dh_data *h = sdev->handler_data;
 165 
 166         if (h->path_state != HP_SW_PATH_ACTIVE) {
 167                 req->rq_flags |= RQF_QUIET;
 168                 return BLK_STS_IOERR;
 169         }
 170 
 171         return BLK_STS_OK;
 172 }
 173 
 174 /*
 175  * hp_sw_activate - Activate a path
 176  * @sdev: sdev on the path to be activated
 177  *
 178  * The HP Active/Passive firmware is pretty simple;
 179  * the passive path reports NOT READY with sense codes
 180  * 0x04/0x02; a START STOP UNIT command will then
 181  * activate the passive path (and deactivate the
 182  * previously active one).
 183  */
 184 static int hp_sw_activate(struct scsi_device *sdev,
 185                                 activate_complete fn, void *data)
 186 {
 187         int ret = SCSI_DH_OK;
 188         struct hp_sw_dh_data *h = sdev->handler_data;
 189 
 190         ret = hp_sw_tur(sdev, h);
 191 
 192         if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE)
 193                 ret = hp_sw_start_stop(h);
 194 
 195         if (fn)
 196                 fn(data, ret);
 197         return 0;
 198 }
 199 
 200 static int hp_sw_bus_attach(struct scsi_device *sdev)
 201 {
 202         struct hp_sw_dh_data *h;
 203         int ret;
 204 
 205         h = kzalloc(sizeof(*h), GFP_KERNEL);
 206         if (!h)
 207                 return SCSI_DH_NOMEM;
 208         h->path_state = HP_SW_PATH_UNINITIALIZED;
 209         h->retries = HP_SW_RETRIES;
 210         h->sdev = sdev;
 211 
 212         ret = hp_sw_tur(sdev, h);
 213         if (ret != SCSI_DH_OK)
 214                 goto failed;
 215         if (h->path_state == HP_SW_PATH_UNINITIALIZED) {
 216                 ret = SCSI_DH_NOSYS;
 217                 goto failed;
 218         }
 219 
 220         sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
 221                     HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
 222                     "active":"passive");
 223 
 224         sdev->handler_data = h;
 225         return SCSI_DH_OK;
 226 failed:
 227         kfree(h);
 228         return ret;
 229 }
 230 
 231 static void hp_sw_bus_detach( struct scsi_device *sdev )
 232 {
 233         kfree(sdev->handler_data);
 234         sdev->handler_data = NULL;
 235 }
 236 
 237 static struct scsi_device_handler hp_sw_dh = {
 238         .name           = HP_SW_NAME,
 239         .module         = THIS_MODULE,
 240         .attach         = hp_sw_bus_attach,
 241         .detach         = hp_sw_bus_detach,
 242         .activate       = hp_sw_activate,
 243         .prep_fn        = hp_sw_prep_fn,
 244 };
 245 
 246 static int __init hp_sw_init(void)
 247 {
 248         return scsi_register_device_handler(&hp_sw_dh);
 249 }
 250 
 251 static void __exit hp_sw_exit(void)
 252 {
 253         scsi_unregister_device_handler(&hp_sw_dh);
 254 }
 255 
 256 module_init(hp_sw_init);
 257 module_exit(hp_sw_exit);
 258 
 259 MODULE_DESCRIPTION("HP Active/Passive driver");
 260 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
 261 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */