Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f4267e3 authored by James Bottomley's avatar James Bottomley
Browse files

[SCSI] Update the SCSI state model to allow blocking in the created state



Brian King <brking@linux.vnet.ibm.com> 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: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 0f1d87a2
Loading
Loading
Loading
Loading
+30 −9
Original line number Diff line number Diff line
@@ -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 */
		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) {
		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) {
		err = scsi_device_set_state(sdev, SDEV_CREATED);

		if (err)
			return err;
	}

	spin_lock_irqsave(q->queue_lock, flags);
	blk_start_queue(q);
+14 −2
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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)
+9 −5
Original line number Diff line number Diff line
@@ -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 */