From 01b291bd66564b4bd826326af6bd0b6d17e99439 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 21 Aug 2008 15:14:14 -0500 Subject: [SCSI] fix check of PQ and PDT bits for WLUNs For IBM z series certain LUNs can no longer be accessed. This is because kernel version 2.6.19 a check was introduced not to create a generic SCSI device for devices that return PQ=1 and PDT=0x1f. For WLUNs (see SAM-3, p. 41ff) generic SCSI devices should be created unconditionally without looking at the PQ bit, so add a check for WLUNs in with this test. Acked-by: Martin Petermann Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/scsi_scan.c') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 84b4879cff11..34d0de6cd511 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1080,7 +1080,8 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, * PDT=1Fh none (no FDD connected to the requested logical unit) */ if (((result[0] >> 5) == 1 || starget->pdt_1f_for_no_lun) && - (result[0] & 0x1f) == 0x1f) { + (result[0] & 0x1f) == 0x1f && + !scsi_is_wlun(lun)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: peripheral device type" " of 31, no device added\n")); -- cgit v1.2.3 From 0f1d87a2acb8fd1f2ef8af109a785123ddc1a6cb Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Aug 2008 16:43:59 -0500 Subject: [SCSI] add inline functions for recognising created and blocked states The created and blocked states are very shortly going to correspond to mixed sdev_state states. Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 13 +++++++------ drivers/scsi/scsi_scan.c | 4 ++-- include/scsi/scsi_device.h | 11 +++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/scsi_scan.c') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ee6be596503d..762a8797e0be 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -664,13 +664,14 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) goto out; } - /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */ - if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) { + /* Check to see if the scsi lld made this device blocked. */ + if (unlikely(scsi_device_blocked(cmd->device))) { /* - * in SDEV_BLOCK, the command is just put back on the device - * queue. The suspend state has already blocked the queue so - * future requests should not occur until the device - * transitions out of the suspend state. + * in blocked state, the command is just put back on + * the device queue. The suspend state has already + * blocked the queue so future requests should not + * occur until the device transitions out of the + * suspend state. */ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 34d0de6cd511..2926baaac31e 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -994,7 +994,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, */ sdev = scsi_device_lookup_by_target(starget, lun); if (sdev) { - if (rescan || sdev->sdev_state != SDEV_CREATED) { + if (rescan || !scsi_device_created(sdev)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: device exists on %s\n", sdev->sdev_gendev.bus_id)); @@ -1467,7 +1467,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, kfree(lun_data); out: scsi_device_put(sdev); - if (sdev->sdev_state == SDEV_CREATED) + if (scsi_device_created(sdev)) /* * the sdev we used didn't appear in the report luns scan */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 80b2e93c2936..cc46652e4658 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -384,10 +384,21 @@ static inline unsigned int sdev_id(struct scsi_device *sdev) #define scmd_id(scmd) sdev_id((scmd)->device) #define scmd_channel(scmd) sdev_channel((scmd)->device) +/* + * checks for positions of the SCSI state machine + */ static inline int scsi_device_online(struct scsi_device *sdev) { return sdev->sdev_state != SDEV_OFFLINE; } +static inline int scsi_device_blocked(struct scsi_device *sdev) +{ + return sdev->sdev_state == SDEV_BLOCK; +} +static inline int scsi_device_created(struct scsi_device *sdev) +{ + return sdev->sdev_state == SDEV_CREATED; +} /* accessor functions for the SCSI parameters */ static inline int scsi_device_sync(struct scsi_device *sdev) -- cgit v1.2.3 From 6f4267e3bd1211b3d09130e626b0b3d885077610 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Aug 2008 16:53:31 -0500 Subject: [SCSI] Update the SCSI state model to allow blocking in the created state Brian King reported that fibre channel devices can oops during scanning if their ports block (because the device goes from CREATED -> BLOCK -> RUNNING rather than CREATED -> BLOCK -> CREATED). Fix this by adding a new state: CREATED_BLOCK which can only transition back to CREATED and disallow the CREATED -> BLOCK transition. Now both the created and blocked states that the mid-layer recognises can include CREATED_BLOCK. Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 39 ++++++++++++++++++++++++++++++--------- drivers/scsi/scsi_scan.c | 16 ++++++++++++++-- drivers/scsi/scsi_sysfs.c | 1 + include/scsi/scsi_device.h | 14 +++++++++----- 4 files changed, 54 insertions(+), 16 deletions(-) (limited to 'drivers/scsi/scsi_scan.c') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 62307bd794a9..d2884bffa1b9 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1251,6 +1251,7 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) break; case SDEV_QUIESCE: case SDEV_BLOCK: + case SDEV_CREATED_BLOCK: /* * If the devices is blocked we defer normal commands. */ @@ -2064,10 +2065,13 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) switch (state) { case SDEV_CREATED: - /* There are no legal states that come back to - * created. This is the manually initialised start - * state */ - goto illegal; + switch (oldstate) { + case SDEV_CREATED_BLOCK: + break; + default: + goto illegal; + } + break; case SDEV_RUNNING: switch (oldstate) { @@ -2105,8 +2109,17 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) case SDEV_BLOCK: switch (oldstate) { - case SDEV_CREATED: case SDEV_RUNNING: + case SDEV_CREATED_BLOCK: + break; + default: + goto illegal; + } + break; + + case SDEV_CREATED_BLOCK: + switch (oldstate) { + case SDEV_CREATED: break; default: goto illegal; @@ -2394,8 +2407,12 @@ scsi_internal_device_block(struct scsi_device *sdev) int err = 0; err = scsi_device_set_state(sdev, SDEV_BLOCK); - if (err) - return err; + if (err) { + err = scsi_device_set_state(sdev, SDEV_CREATED_BLOCK); + + if (err) + return err; + } /* * The device has transitioned to SDEV_BLOCK. Stop the @@ -2438,8 +2455,12 @@ scsi_internal_device_unblock(struct scsi_device *sdev) * and goose the device queue if successful. */ err = scsi_device_set_state(sdev, SDEV_RUNNING); - if (err) - return err; + if (err) { + err = scsi_device_set_state(sdev, SDEV_CREATED); + + if (err) + return err; + } spin_lock_irqsave(q->queue_lock, flags); blk_start_queue(q); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 2926baaac31e..334862e26a1b 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -730,6 +730,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, int *bflags, int async) { + int ret; + /* * XXX do not save the inquiry, since it can change underneath us, * save just vendor/model/rev. @@ -885,7 +887,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, /* set the device running here so that slave configure * may do I/O */ - scsi_device_set_state(sdev, SDEV_RUNNING); + ret = scsi_device_set_state(sdev, SDEV_RUNNING); + if (ret) { + ret = scsi_device_set_state(sdev, SDEV_BLOCK); + + if (ret) { + sdev_printk(KERN_ERR, sdev, + "in wrong state %s to complete scan\n", + scsi_device_state_name(sdev->sdev_state)); + return SCSI_SCAN_NO_RESPONSE; + } + } if (*bflags & BLIST_MS_192_BYTES_FOR_3F) sdev->use_192_bytes_for_3f = 1; @@ -899,7 +911,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, transport_configure_device(&sdev->sdev_gendev); if (sdev->host->hostt->slave_configure) { - int ret = sdev->host->hostt->slave_configure(sdev); + ret = sdev->host->hostt->slave_configure(sdev); if (ret) { /* * if LLDD reports slave not present, don't clutter diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ab3c71869be5..09d311d559d1 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -34,6 +34,7 @@ static const struct { { SDEV_QUIESCE, "quiesce" }, { SDEV_OFFLINE, "offline" }, { SDEV_BLOCK, "blocked" }, + { SDEV_CREATED_BLOCK, "created-blocked" }, }; const char *scsi_device_state_name(enum scsi_device_state state) diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index cc46652e4658..b49e725be039 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -42,9 +42,11 @@ enum scsi_device_state { * originate in the mid-layer) */ SDEV_OFFLINE, /* Device offlined (by error handling or * user request */ - SDEV_BLOCK, /* Device blocked by scsi lld. No scsi - * commands from user or midlayer should be issued - * to the scsi lld. */ + SDEV_BLOCK, /* Device blocked by scsi lld. No + * scsi commands from user or midlayer + * should be issued to the scsi + * lld. */ + SDEV_CREATED_BLOCK, /* same as above but for created devices */ }; enum scsi_device_event { @@ -393,11 +395,13 @@ static inline int scsi_device_online(struct scsi_device *sdev) } static inline int scsi_device_blocked(struct scsi_device *sdev) { - return sdev->sdev_state == SDEV_BLOCK; + return sdev->sdev_state == SDEV_BLOCK || + sdev->sdev_state == SDEV_CREATED_BLOCK; } static inline int scsi_device_created(struct scsi_device *sdev) { - return sdev->sdev_state == SDEV_CREATED; + return sdev->sdev_state == SDEV_CREATED || + sdev->sdev_state == SDEV_CREATED_BLOCK; } /* accessor functions for the SCSI parameters */ -- cgit v1.2.3