Subject: sd(4) block size detection (Re: SCSI Mode Sense on page 0)
To: None <bouyer@antioche.eu.org>
From: ITOH Yasufumi <itohy@NetBSD.org>
List: tech-kern
Date: 11/25/2006 02:58:07
bouyer@antioche.eu.org writes:
> On Sun, Nov 19, 2006 at 10:05:34PM +0900, ITOH Yasufumi wrote:
> > Hello,
> >
> > Tracking down kern/26537, I noticed our cd(4) and sd(4)
> > drivers issue SCSI Mode Sense command on page 0.
> >
> > The SCSI specification says the page 0 is ``vendor-specific'' and
> > even the parameter format may be non-standard.
> >
> > For what reasons our drivers use such a vendor-specific behavior?
>
> I suspect it was the way to retrieve the block size on older scsi
> devices. Maybe we should try to use READ_FORMAT_CAPACITIES before going
> to Mode Sense page 0.
I noticed the page contents of Mode Sense is not used, but
the block descriptor (sent prior to the page contents) is used,
so it is not that vendor-specific (but this device don't like it).
On the contrary the Read Format Capacities command seems vendor-specific.
Hmm, then, I'd like to change current method of detecting block size
if Read Capacity failed
use Read Format Capacities
if invalid use 512
else
use Mode Sense page 0
if failed, use value of Read Capacity or 512 if invalid
to a new method:
if Read Capacity failed
use Read Format Capacities
else if value of Read Capacity is invalid
use Mode Sense 0
if above block size is invalid
use 512
We don't support multiple block sizes per LUN, and if Read Capacity
reports valid block size, why don't we use it?
Is this change OK?
By the way, scsipi_size() used only by the sd driver,
this patch also moves its function to sd.c.
--
ITOH Yasufumi
Index: scsipi_base.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsipi_base.c,v
retrieving revision 1.140
diff -u -p -r1.140 scsipi_base.c
--- scsipi_base.c 20 Oct 2006 07:11:50 -0000 1.140
+++ scsipi_base.c 24 Nov 2006 17:41:13 -0000
@@ -1021,100 +1021,6 @@ scsipi_interpret_sense(struct scsipi_xfe
}
/*
- * scsipi_validate_secsize:
- *
- * Validate the sector size reported by READ_CAPACITY_1[06].
- * Use the supplied default if the reported size looks wrong.
- */
-static int
-scsipi_validate_secsize(struct scsipi_periph *periph, const char *opcode,
- int raw_len, int def_len)
-{
-
- switch (raw_len) {
- case 256:
- case 512:
- case 1024:
- case 2048:
- case 4096:
- break;
-
- default:
- scsipi_printaddr(periph);
- printf("%s returned %s sector size: 0x%x. Defaulting to %d "
- "bytes.\n", opcode, (raw_len ^ (1 << (ffs(raw_len) - 1))) ?
- "preposterous" : "unsupported", raw_len, def_len);
- /*FALLTHROUGH*/
- case 0:
- raw_len = def_len;
- break;
- }
-
- return (raw_len);
-}
-
-/*
- * scsipi_size:
- *
- * Find out from the device what its capacity is.
- */
-u_int64_t
-scsipi_size(struct scsipi_periph *periph, int *secsize, int defsize, int flags)
-{
- union {
- struct scsipi_read_capacity_10 cmd;
- struct scsipi_read_capacity_16 cmd16;
- } cmd;
- union {
- struct scsipi_read_capacity_10_data data;
- struct scsipi_read_capacity_16_data data16;
- } data;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd.opcode = READ_CAPACITY_10;
-
- /*
- * If the command works, interpret the result as a 4 byte
- * number of blocks
- */
- if (scsipi_command(periph, (void *)&cmd.cmd, sizeof(cmd.cmd),
- (void *)&data.data, sizeof(data.data), SCSIPIRETRIES, 20000, NULL,
- flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK | XS_CTL_SILENT) != 0)
- return (0);
-
- if (_4btol(data.data.addr) != 0xffffffff) {
- if (secsize) {
- *secsize = scsipi_validate_secsize(periph,
- "READ_CAPACITY_10", _4btol(data.data.length),
- defsize);
- }
- return (_4btol(data.data.addr) + 1);
- }
-
- /*
- * Device is larger than can be reflected by READ CAPACITY (10).
- * Try READ CAPACITY (16).
- */
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.cmd16.opcode = READ_CAPACITY_16;
- cmd.cmd16.byte2 = SRC16_SERVICE_ACTION;
- _lto4b(sizeof(data.data16), cmd.cmd16.len);
-
- if (scsipi_command(periph, (void *)&cmd.cmd16, sizeof(cmd.cmd16),
- (void *)&data.data16, sizeof(data.data16), SCSIPIRETRIES, 20000,
- NULL,
- flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK | XS_CTL_SILENT) != 0)
- return (0);
-
- if (secsize) {
- *secsize = scsipi_validate_secsize(periph, "READ_CAPACITY_16",
- _4btol(data.data16.length), defsize);
- }
- return (_8btol(data.data16.addr) + 1);
-}
-
-/*
* scsipi_test_unit_ready:
*
* Issue a `test unit ready' request.
Index: scsipiconf.h
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/scsipiconf.h,v
retrieving revision 1.106
diff -u -p -r1.106 scsipiconf.h
--- scsipiconf.h 30 Oct 2006 16:15:56 -0000 1.106
+++ scsipiconf.h 24 Nov 2006 17:41:13 -0000
@@ -633,7 +633,6 @@ const void *scsipi_inqmatch(struct scsip
const char *scsipi_dtype(int);
void scsipi_strvis(u_char *, int, const u_char *, int);
int scsipi_execute_xs(struct scsipi_xfer *);
-u_int64_t scsipi_size(struct scsipi_periph *, int *, int, int);
int scsipi_test_unit_ready(struct scsipi_periph *, int);
int scsipi_prevent(struct scsipi_periph *, int, int);
int scsipi_inquire(struct scsipi_periph *,
Index: sd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/scsipi/sd.c,v
retrieving revision 1.253
diff -u -p -r1.253 sd.c
--- sd.c 20 Oct 2006 07:11:50 -0000 1.253
+++ sd.c 24 Nov 2006 17:41:13 -0000
@@ -97,6 +97,8 @@ __KERNEL_RCSID(0, "$NetBSD: sd.c,v 1.253
#define SDLABELDEV(dev) (MAKESDDEV(major(dev), SDUNIT(dev), RAW_PART))
+#define SD_DEFAULT_SECSIZE 512
+
static void sdminphys(struct buf *);
static void sdgetdefaultlabel(struct sd_softc *, struct disklabel *);
static int sdgetdisklabel(struct sd_softc *);
@@ -110,6 +112,8 @@ static int sd_mode_sense(struct sd_softc
int, int *);
static int sd_mode_select(struct sd_softc *, u_int8_t, void *, size_t, int,
int);
+static int sd_validate_secsize(struct scsipi_periph *, int);
+static u_int64_t sd_read_capacity(struct scsipi_periph *, int *, int flags);
static int sd_get_simplifiedparms(struct sd_softc *, struct disk_parms *,
int);
static int sd_get_capacity(struct sd_softc *, struct disk_parms *, int);
@@ -1636,6 +1640,91 @@ sd_mode_select(struct sd_softc *sd, u_in
}
}
+/*
+ * sd_validate_secsize:
+ *
+ * Validate the sector size.
+ */
+static int
+sd_validate_secsize(struct scsipi_periph *periph, int len)
+{
+
+ switch (len) {
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ case 4096:
+ return 1;
+ }
+
+ if (periph) {
+ scsipi_printaddr(periph);
+ printf("%s sector size: 0x%x. Defaulting to %d bytes.\n",
+ (len ^ (1 << (ffs(len) - 1))) ?
+ "preposterous" : "unsupported",
+ len, SD_DEFAULT_SECSIZE);
+ }
+
+ return 0;
+}
+
+/*
+ * sd_read_capacity:
+ *
+ * Find out from the device what its capacity is.
+ */
+static u_int64_t
+sd_read_capacity(struct scsipi_periph *periph, int *secsize, int flags)
+{
+ union {
+ struct scsipi_read_capacity_10 cmd;
+ struct scsipi_read_capacity_16 cmd16;
+ } cmd;
+ union {
+ struct scsipi_read_capacity_10_data data;
+ struct scsipi_read_capacity_16_data data16;
+ } data;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd.opcode = READ_CAPACITY_10;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ memset(&data.data, 0, sizeof(data.data));
+ if (scsipi_command(periph, (void *)&cmd.cmd, sizeof(cmd.cmd),
+ (void *)&data.data, sizeof(data.data), SCSIPIRETRIES, 20000, NULL,
+ flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK | XS_CTL_SILENT) != 0)
+ return (0);
+
+ if (_4btol(data.data.addr) != 0xffffffff) {
+ *secsize = _4btol(data.data.length);
+ return (_4btol(data.data.addr) + 1);
+ }
+
+ /*
+ * Device is larger than can be reflected by READ CAPACITY (10).
+ * Try READ CAPACITY (16).
+ */
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd16.opcode = READ_CAPACITY_16;
+ cmd.cmd16.byte2 = SRC16_SERVICE_ACTION;
+ _lto4b(sizeof(data.data16), cmd.cmd16.len);
+
+ memset(&data.data16, 0, sizeof(data.data16));
+ if (scsipi_command(periph, (void *)&cmd.cmd16, sizeof(cmd.cmd16),
+ (void *)&data.data16, sizeof(data.data16), SCSIPIRETRIES, 20000,
+ NULL,
+ flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK | XS_CTL_SILENT) != 0)
+ return (0);
+
+ *secsize = _4btol(data.data16.length);
+ return (_8btol(data.data16.addr) + 1);
+}
+
static int
sd_get_simplifiedparms(struct sd_softc *sd, struct disk_parms *dp, int flags)
{
@@ -1655,15 +1744,17 @@ sd_get_simplifiedparms(struct sd_softc *
int error, blksize;
/*
- * scsipi_size (ie "read capacity") and mode sense page 6
+ * sd_read_capacity (ie "read capacity") and mode sense page 6
* give the same information. Do both for now, and check
* for consistency.
* XXX probably differs for removable media
*/
- dp->blksize = 512;
- if ((sectors = scsipi_size(sd->sc_periph, &blksize, 512, flags)) == 0)
+ dp->blksize = SD_DEFAULT_SECSIZE;
+ if ((sectors = sd_read_capacity(sd->sc_periph, &blksize, flags)) == 0)
return (SDGP_RESULT_OFFLINE); /* XXX? */
+ dp->blksize = blksize;
+
error = scsipi_mode_sense(sd->sc_periph, SMS_DBD, 6,
&scsipi_sense.header, sizeof(scsipi_sense),
flags | XS_CTL_DATA_ONSTACK, SDRETRIES, 6000);
@@ -1671,9 +1762,10 @@ sd_get_simplifiedparms(struct sd_softc *
if (error != 0)
return (SDGP_RESULT_OFFLINE); /* XXX? */
- dp->blksize = _2btol(scsipi_sense.lbs);
- if (dp->blksize == 0)
- dp->blksize = blksize;
+ if (!sd_validate_secsize(NULL, dp->blksize))
+ dp->blksize = _2btol(scsipi_sense.lbs);
+ if (!sd_validate_secsize(sd->sc_periph, dp->blksize))
+ dp->blksize = SD_DEFAULT_SECSIZE;
/*
* Create a pseudo-geometry.
@@ -1707,7 +1799,7 @@ sd_get_capacity(struct sd_softc *sd, str
u_int8_t *p;
#endif
- dp->disksize = sectors = scsipi_size(sd->sc_periph, &blksize, 512,
+ dp->disksize = sectors = sd_read_capacity(sd->sc_periph, &blksize,
flags);
if (sectors == 0) {
struct scsipi_read_format_capacities cmd;
@@ -1752,18 +1844,16 @@ printf("rfc result:"); for (i = sizeof(s
if (sectors == 0)
return (SDGP_RESULT_OFFLINE); /* XXX? */
- dp->blksize = _3btol(data.desc.blklen);
- if (dp->blksize == 0)
- dp->blksize = 512;
- } else {
+ blksize = _3btol(data.desc.blklen);
+
+ } else if (!sd_validate_secsize(NULL, blksize)) {
struct sd_mode_sense_data scsipi_sense;
int big, bsize;
struct scsi_general_block_descriptor *bdesc;
memset(&scsipi_sense, 0, sizeof(scsipi_sense));
error = sd_mode_sense(sd, 0, &scsipi_sense,
- sizeof(scsipi_sense.blk_desc), 0, flags | XS_CTL_SILENT, &big);
- dp->blksize = blksize;
+ sizeof(scsipi_sense.blk_desc), 0x3f, flags | XS_CTL_SILENT, &big);
if (!error) {
if (big) {
bdesc = (void *)(&scsipi_sense.header.big + 1);
@@ -1780,13 +1870,15 @@ printf("page 0 ok\n");
#endif
if (bsize >= 8) {
- dp->blksize = _3btol(bdesc->blklen);
- if (dp->blksize == 0)
- dp->blksize = blksize;
+ blksize = _3btol(bdesc->blklen);
}
}
}
+ if (!sd_validate_secsize(sd->sc_periph, blksize))
+ blksize = SD_DEFAULT_SECSIZE;
+
+ dp->blksize = blksize;
dp->disksize512 = (sectors * dp->blksize) / DEV_BSIZE;
return (0);
}