This patch updates the hp_sw device handler to properly check the return codes etc. And we now even free up the request ... Signed-off-by: Hannes Reinecke <hare@xxxxxxx> --- drivers/scsi/device_handler/scsi_dh_hp_sw.c | 212 +++++++++++++++++++++++--- 1 files changed, 187 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index d5d7906..a44b319 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -25,13 +25,18 @@ #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> -#define HP_SW_NAME "hp_sw" +#define HP_SW_NAME "hp_sw" -#define HP_SW_TIMEOUT (60 * HZ) -#define HP_SW_RETRIES 3 +#define HP_SW_TIMEOUT (60 * HZ) +#define HP_SW_RETRIES 3 + +#define HP_SW_PATH_UNINITIALIZED -1 +#define HP_SW_PATH_ACTIVE 0 +#define HP_SW_PATH_PASSIVE 1 struct hp_sw_dh_data { unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + int path_state; int retries; }; @@ -42,41 +47,137 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) return ((struct hp_sw_dh_data *) scsi_dh_data->buf); } -static int hp_sw_done(struct scsi_device *sdev) +static int tur_done(struct scsi_device *sdev, int errors) +{ + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + struct scsi_sense_hdr sshdr; + int ret; + + if (status_byte(errors) != CHECK_CONDITION) { + sdev_printk(KERN_WARNING, sdev, + "%s: sending tur failed with %x\n", + HP_SW_NAME, errors); + ret = SCSI_DH_IO; + goto done; + } + ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); + if (!ret) { + ret = SCSI_DH_IO; + goto done; + } + switch (sshdr.sense_key) { + case UNIT_ATTENTION: + ret = SCSI_DH_IMM_RETRY; + break; + case NOT_READY: + if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) { + /* + * LUN not ready - Initialization command required + * + * This is the passive path + */ + h->path_state = HP_SW_PATH_PASSIVE; + ret = SCSI_DH_OK; + break; + } + /* Fallthrough */ + default: + sdev_printk(KERN_WARNING, sdev, + "%s: sending tur failed, sense %x/%x/%x\n", + HP_SW_NAME, sshdr.sense_key, sshdr.asc, + sshdr.ascq); + break; + } + +done: + return ret; +} + +static int hp_sw_tur(struct scsi_device *sdev) +{ + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + struct request *req; + int ret = SCSI_DH_RES_TEMP_UNAVAIL; + + req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC); + if (!req) + goto done; + + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_flags |= REQ_FAILFAST; + req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); + memset(req->cmd, 0, MAX_COMMAND_SIZE); + req->cmd[0] = TEST_UNIT_READY; + req->timeout = HP_SW_TIMEOUT; + req->sense = h->sense; + memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); + req->sense_len = 0; + +retry: + ret = blk_execute_rq(req->q, NULL, req, 1); + if (ret == -EIO) { + ret = tur_done(sdev, req->errors); + } else { + h->path_state = HP_SW_PATH_ACTIVE; + ret = SCSI_DH_OK; + } + if (ret == SCSI_DH_IMM_RETRY) + goto retry; + + blk_put_request(req); + +done: + return ret; +} + +static int start_done(struct scsi_device *sdev, int errors) { struct hp_sw_dh_data *h = get_hp_sw_data(sdev); struct scsi_sense_hdr sshdr; int rc; - sdev_printk(KERN_INFO, sdev, "hp_sw_done\n"); + if (status_byte(errors) != CHECK_CONDITION) { + sdev_printk(KERN_WARNING, sdev, + "%s: sending start_stop_unit failed with %x\n", + HP_SW_NAME, errors); + rc = SCSI_DH_IO; + goto done; + } rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr); - if (!rc) + if (!rc) { + rc = SCSI_DH_IO; goto done; + } switch (sshdr.sense_key) { case NOT_READY: - if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) { + if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) { + /* + * LUN not ready - initialization command required + * + * Switch-over in progress, retry. + */ rc = SCSI_DH_RETRY; - h->retries++; + h->retries--; break; } /* fall through */ default: - h->retries++; - rc = SCSI_DH_IMM_RETRY; + sdev_printk(KERN_WARNING, sdev, + "%s: sending start_stop_unit failed, sense %x/%x/%x\n", + HP_SW_NAME, sshdr.sense_key, sshdr.asc, + sshdr.ascq); + h->retries--; + rc = SCSI_DH_RETRY; } + if (rc == SCSI_DH_RETRY && !h->retries) + rc = SCSI_DH_IO; done: - if (rc == SCSI_DH_OK || rc == SCSI_DH_IO) - h->retries = 0; - else if (h->retries > HP_SW_RETRIES) { - h->retries = 0; - rc = SCSI_DH_IO; - } return rc; } -static int hp_sw_activate(struct scsi_device *sdev) +static int hp_sw_start_stop(struct scsi_device *sdev) { struct hp_sw_dh_data *h = get_hp_sw_data(sdev); struct request *req; @@ -86,8 +187,6 @@ static int hp_sw_activate(struct scsi_device *sdev) if (!req) goto done; - sdev_printk(KERN_INFO, sdev, "sending START_STOP."); - req->cmd_type = REQ_TYPE_BLOCK_PC; req->cmd_flags |= REQ_FAILFAST; req->cmd_len = COMMAND_SIZE(START_STOP); @@ -99,17 +198,55 @@ static int hp_sw_activate(struct scsi_device *sdev) memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); req->sense_len = 0; +retry: ret = blk_execute_rq(req->q, NULL, req, 1); - if (!ret) /* SUCCESS */ - ret = hp_sw_done(sdev); + if (ret == -EIO) + ret = start_done(sdev, req->errors); else ret = SCSI_DH_IO; + + if (ret == SCSI_DH_RETRY) + goto retry; + + blk_put_request(req); done: return ret; } +static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) +{ + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + int ret = BLKPREP_OK; + + if (h->path_state != HP_SW_PATH_ACTIVE) { + ret = BLKPREP_KILL; + req->cmd_flags |= REQ_QUIET; + } + return ret; + +} + +static int hp_sw_activate(struct scsi_device *sdev) +{ + int ret = SCSI_DH_OK; + struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + + ret = hp_sw_tur(sdev); + + if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) { + ret = hp_sw_start_stop(sdev); + if (ret == SCSI_DH_OK) + sdev_printk(KERN_INFO, sdev, + "%s: activated path\n", + HP_SW_NAME); + } + + return ret; +} + const struct scsi_dh_devlist hp_sw_dh_data_list[] = { - {"COMPAQ", "MSA"}, + {"COMPAQ", "MSA1000"}, + {"COMPAQ", "HSV110"}, {"HP", "HSV100"}, {"DEC", "HSG80"}, {NULL, NULL}, @@ -125,30 +262,55 @@ static struct scsi_device_handler hp_sw_dh = { .attach = hp_sw_bus_attach, .detach = hp_sw_bus_detach, .activate = hp_sw_activate, + .prep_fn = hp_sw_prep_fn, }; static int hp_sw_bus_attach(struct scsi_device *sdev) { struct scsi_dh_data *scsi_dh_data; + struct hp_sw_dh_data *h; unsigned long flags; + int ret; scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); if (!scsi_dh_data) { - sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n", + sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n", HP_SW_NAME); return 0; } + h = (struct hp_sw_dh_data *) scsi_dh_data->buf; + h->path_state = HP_SW_PATH_UNINITIALIZED; + h->retries = HP_SW_RETRIES; + scsi_dh_data->scsi_dh = &hp_sw_dh; spin_lock_irqsave(sdev->request_queue->queue_lock, flags); sdev->scsi_dh_data = scsi_dh_data; spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + + ret = hp_sw_tur(sdev); + if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) + goto failed; + + sdev_printk(KERN_INFO, sdev, "%s: %s path\n", + HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? + "active":"passive"); + try_module_get(THIS_MODULE); - sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME); + sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", HP_SW_NAME); return 0; + +failed: + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); + sdev->scsi_dh_data = NULL; + spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + kfree(scsi_dh_data); + sdev_printk(KERN_ERR, sdev, "%s: not attached\n", + HP_SW_NAME); + return -EINVAL; } static void hp_sw_bus_detach( struct scsi_device *sdev ) @@ -166,7 +328,7 @@ static void hp_sw_bus_detach( struct scsi_device *sdev ) spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); module_put(THIS_MODULE); - sdev_printk(KERN_NOTICE, sdev, "Detached %s\n", HP_SW_NAME); + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME); kfree(scsi_dh_data); } -- 1.5.2.4 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel