summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authorken <ken@FreeBSD.org>2003-04-30 00:35:22 +0000
committerken <ken@FreeBSD.org>2003-04-30 00:35:22 +0000
commit3c97032a19ab84c02e1c713a4ac1be1fcfbd98dc (patch)
treec02c49de4d73800f6904f6926dce054d9209a7e0 /sys/cam
parentc761300afad34d68194be31c4e0de85401fc3513 (diff)
downloadFreeBSD-src-3c97032a19ab84c02e1c713a4ac1be1fcfbd98dc.zip
FreeBSD-src-3c97032a19ab84c02e1c713a4ac1be1fcfbd98dc.tar.gz
Add support to CAM for devices with more than 2^32 blocks. (2TB if you're
using 512 byte blocks). cam_ccb.h: Bump up volume_size and cylinders in ccb_calc_geometry to 64 bits and 32 bits respectively, so we can hold larger device sizes. cylinders would overflow at about 500GB. Bump CAM_VERSION for this change. Note that this will require a recompile of all applications that talk to the pass(4) driver. scsi_all.c: Add descriptions for READ/WRITE(16), update READ/WRITE(12) descriptions, add descriptions for SERVICE ACTION IN/OUT. Add a new function, scsi_read_capacity_16(), that issues the read capacity service action. (Necessary for arrays larger than 2^32 sectors.) Update scsi_read_write() to use a 64 bit LBA and issue READ(16) or WRITE(16) if necessary. NOTE the API change. This should be largely transparnet to most userland applications at compile time, but will break binary compatibility. The CAM_VERSION bump, above, also serves the purpose of forcing a recompile for any applications that talk to CAM. scsi_all.h: Add 16 byte READ/WRITE structures, structures for 16 byte READ CAPACITY/SERVICE ACTION IN. Add scsi_u64to8b() and scsi_8btou64. scsi_da.c: The da(4) driver probe now has two stages for devices larger than 2TB. If a standard READ CAPACITY(10) returns 0xffffffff, we issue the 16 byte version of read capacity to determine the true array capacity. We also do the same thing in daopen() -- use the 16 byte read capacity if the device is large enough. The sysctl/loader code has also been updated to accept 16 bytes as a minimum command size.
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/cam_ccb.h6
-rw-r--r--sys/cam/scsi/scsi_all.c82
-rw-r--r--sys/cam/scsi/scsi_all.h88
-rw-r--r--sys/cam/scsi/scsi_da.c259
4 files changed, 347 insertions, 88 deletions
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index 706bbf9..1df01ce 100644
--- a/sys/cam/cam_ccb.h
+++ b/sys/cam/cam_ccb.h
@@ -488,7 +488,7 @@ struct ccb_dev_match {
/*
* Definitions for the path inquiry CCB fields.
*/
-#define CAM_VERSION 0x14 /* Hex value for current version */
+#define CAM_VERSION 0x15 /* Hex value for current version */
typedef enum {
PI_MDP_ABLE = 0x80, /* Supports MDP message */
@@ -789,8 +789,8 @@ struct ccb_trans_settings {
struct ccb_calc_geometry {
struct ccb_hdr ccb_h;
u_int32_t block_size;
- u_int32_t volume_size;
- u_int16_t cylinders;
+ u_int64_t volume_size;
+ u_int32_t cylinders;
u_int8_t heads;
u_int8_t secs_per_track;
};
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index 0f4100d..fe581af 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -2,7 +2,7 @@
* Implementation of Utility functions for all SCSI device types.
*
* Copyright (c) 1997, 1998, 1999 Justin T. Gibbs.
- * Copyright (c) 1997, 1998 Kenneth D. Merry.
+ * Copyright (c) 1997, 1998, 2003 Kenneth D. Merry.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -507,9 +507,11 @@ static struct op_table_entry scsi_op_codes[] = {
/* 85 */
/* 86 */
/* 87 */
-/* 88 */
+/* 88 MM OO O O READ(16) */
+{0x88, D|T|W|R|O, "READ(16)"},
/* 89 */
-/* 8A */
+/* 8A OM O O O WRITE(16) */
+{0x8A, D|T|W|R|O, "WRITE(16)"},
/* 8B */
/* 8C */
/* 8D */
@@ -529,8 +531,11 @@ static struct op_table_entry scsi_op_codes[] = {
/* 9B */
/* 9C */
/* 9D */
-/* 9E */
-/* 9F */
+/* XXX KDM ALL for these? op-num.txt defines them for none.. */
+/* 9E SERVICE ACTION IN(16) */
+{0x9E, ALL, "SERVICE ACTION IN(16)"},
+/* 9F SERVICE ACTION OUT(16) */
+{0x9F, ALL, "SERVICE ACTION OUT(16)"},
/* A0 OOOOOOOOOOO REPORT LUNS */
{0xA0, ALL & ~(E), "REPORT LUNS"},
@@ -560,16 +565,16 @@ static struct op_table_entry scsi_op_codes[] = {
/* A7 OO OO OO MOVE MEDIUM ATTACHED */
{0xA7, D|T|W|R|O|M, "MOVE MEDIUM ATTACHED"},
-/* A8 OM O READ(12) */
-{0xA8, W|R|O, "READ(12)"},
+/* A8 O OM O READ(12) */
+{0xA8,D|W|R|O, "READ(12)"},
/* A8 O GET MESSAGE(12) */
{0xA8, C, "GET MESSAGE(12)"},
/* A9 O PLAY TRACK RELATIVE(12) */
{0xA9, R, "PLAY TRACK RELATIVE(12)"},
-/* AA O O WRITE(12) */
-{0xAA, W|O, "WRITE(12)"},
+/* AA O O O WRITE(12) */
+{0xAA,D|W|O, "WRITE(12)"},
/* AA O WRITE CD(12) {MMC Proposed} */
{0xAA, R, "WRITE CD(12) {MMC Proposed}"},
/* AA O SEND MESSAGE(12) */
@@ -2699,6 +2704,38 @@ scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries,
}
void
+scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, uint64_t lba, int reladr, int pmi,
+ struct scsi_read_capacity_data_long *rcap_buf,
+ uint8_t sense_len, uint32_t timeout)
+{
+ struct scsi_read_capacity_16 *scsi_cmd;
+
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ /*data_ptr*/(u_int8_t *)rcap_buf,
+ /*dxfer_len*/sizeof(*rcap_buf),
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+ scsi_cmd = (struct scsi_read_capacity_16 *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+ scsi_cmd->opcode = SERVICE_ACTION_IN;
+ scsi_cmd->service_action = SRC16_SERVICE_ACTION;
+ scsi_u64to8b(lba, scsi_cmd->addr);
+ scsi_ulto4b(sizeof(*rcap_buf), scsi_cmd->alloc_len);
+ if (pmi)
+ reladr |= SRC16_PMI;
+ if (reladr)
+ reladr |= SRC16_RELADR;
+}
+
+void
scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, struct scsi_report_luns_data *rpl_buf,
@@ -2758,7 +2795,7 @@ void
scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, int readop, u_int8_t byte2,
- int minimum_cmd_size, u_int32_t lba, u_int32_t block_count,
+ int minimum_cmd_size, u_int64_t lba, u_int32_t block_count,
u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len,
u_int32_t timeout)
{
@@ -2790,7 +2827,8 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->addr[1], scsi_cmd->addr[2],
scsi_cmd->length, dxfer_len));
} else if ((minimum_cmd_size < 12)
- && ((block_count & 0xffff) == block_count)) {
+ && ((block_count & 0xffff) == block_count)
+ && ((lba & 0xffffffff) == lba)) {
/*
* Need a 10 byte cdb.
*/
@@ -2810,11 +2848,12 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->addr[1], scsi_cmd->addr[2],
scsi_cmd->addr[3], scsi_cmd->length[0],
scsi_cmd->length[1], dxfer_len));
- } else {
+ } else if ((minimum_cmd_size < 16)
+ && ((block_count & 0xffffffff) == block_count)
+ && ((lba & 0xffffffff) == lba)) {
/*
* The block count is too big for a 10 byte CDB, use a 12
- * byte CDB. READ/WRITE(12) are currently only defined for
- * optical devices.
+ * byte CDB.
*/
struct scsi_rw_12 *scsi_cmd;
@@ -2833,6 +2872,21 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->addr[3], scsi_cmd->length[0],
scsi_cmd->length[1], scsi_cmd->length[2],
scsi_cmd->length[3], dxfer_len));
+ } else {
+ /*
+ * 16 byte CDB. We'll only get here if the LBA is larger
+ * than 2^32, or if the user asks for a 16 byte command.
+ */
+ struct scsi_rw_16 *scsi_cmd;
+
+ scsi_cmd = (struct scsi_rw_16 *)&csio->cdb_io.cdb_bytes;
+ scsi_cmd->opcode = readop ? READ_16 : WRITE_16;
+ scsi_cmd->byte2 = byte2;
+ scsi_u64to8b(lba, scsi_cmd->addr);
+ scsi_cmd->reserved = 0;
+ scsi_ulto4b(block_count, scsi_cmd->length);
+ scsi_cmd->control = 0;
+ cdb_len = sizeof(*scsi_cmd);
}
cam_fill_csio(csio,
retries,
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index b972c5a..9470ee8 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -419,7 +419,9 @@ struct scsi_rw_10
{
u_int8_t opcode;
#define SRW10_RELADDR 0x01
-#define SRW10_FUA 0x08
+/* EBP defined for WRITE(10) only */
+#define SRW10_EBP 0x04
+#define SRW10_FUA 0x08
#define SRW10_DPO 0x10
u_int8_t byte2;
u_int8_t addr[4];
@@ -441,6 +443,19 @@ struct scsi_rw_12
u_int8_t control;
};
+struct scsi_rw_16
+{
+ u_int8_t opcode;
+#define SRW16_RELADDR 0x01
+#define SRW16_FUA 0x08
+#define SRW16_DPO 0x10
+ u_int8_t byte2;
+ u_int8_t addr[8];
+ u_int8_t length[4];
+ u_int8_t reserved;
+ u_int8_t control;
+};
+
struct scsi_start_stop_unit
{
u_int8_t opcode;
@@ -486,11 +501,14 @@ struct scsi_start_stop_unit
#define LOG_SENSE 0x4d
#define MODE_SELECT_10 0x55
#define MODE_SENSE_10 0x5A
+#define READ_16 0x88
+#define WRITE_16 0x8a
+#define SERVICE_ACTION_IN 0x9e
#define REPORT_LUNS 0xA0
-#define MOVE_MEDIUM 0xa5
-#define READ_12 0xa8
-#define WRITE_12 0xaa
-#define READ_ELEMENT_STATUS 0xb8
+#define MOVE_MEDIUM 0xa5
+#define READ_12 0xa8
+#define WRITE_12 0xaa
+#define READ_ELEMENT_STATUS 0xb8
/*
@@ -651,12 +669,31 @@ struct scsi_read_capacity
u_int8_t control;
};
+struct scsi_read_capacity_16
+{
+ uint8_t opcode;
+#define SRC16_SERVICE_ACTION 0x10
+ uint8_t service_action;
+ uint8_t addr[8];
+ uint8_t alloc_len[4];
+#define SRC16_PMI 0x01
+#define SRC16_RELADR 0x02
+ uint8_t reladr;
+ uint8_t control;
+};
+
struct scsi_read_capacity_data
{
u_int8_t addr[4];
u_int8_t length[4];
};
+struct scsi_read_capacity_data_long
+{
+ uint8_t addr[8];
+ uint8_t length[4];
+};
+
struct scsi_report_luns
{
u_int8_t opcode;
@@ -977,6 +1014,13 @@ void scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries,
union ccb *), u_int8_t tag_action,
struct scsi_read_capacity_data *,
u_int8_t sense_len, u_int32_t timeout);
+void scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *,
+ union ccb *), uint8_t tag_action,
+ uint64_t lba, int reladr, int pmi,
+ struct scsi_read_capacity_data_long
+ *rcap_buf, uint8_t sense_len,
+ uint32_t timeout);
void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *,
@@ -995,7 +1039,7 @@ void scsi_synchronize_cache(struct ccb_scsiio *csio,
void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, int readop, u_int8_t byte2,
- int minimum_cmd_size, u_int32_t lba,
+ int minimum_cmd_size, u_int64_t lba,
u_int32_t block_count, u_int8_t *data_ptr,
u_int32_t dxfer_len, u_int8_t sense_len,
u_int32_t timeout);
@@ -1015,10 +1059,12 @@ static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
static __inline void scsi_ulto2b(u_int32_t val, u_int8_t *bytes);
static __inline void scsi_ulto3b(u_int32_t val, u_int8_t *bytes);
static __inline void scsi_ulto4b(u_int32_t val, u_int8_t *bytes);
+static __inline void scsi_u64to8b(u_int64_t val, u_int8_t *bytes);
static __inline u_int32_t scsi_2btoul(u_int8_t *bytes);
static __inline u_int32_t scsi_3btoul(u_int8_t *bytes);
static __inline int32_t scsi_3btol(u_int8_t *bytes);
static __inline u_int32_t scsi_4btoul(u_int8_t *bytes);
+static __inline u_int64_t scsi_8btou64(u_int8_t *bytes);
static __inline void *find_mode_page_6(struct scsi_mode_header_6 *mode_header);
static __inline void *find_mode_page_10(struct scsi_mode_header_10 *mode_header);
@@ -1059,6 +1105,20 @@ scsi_ulto4b(u_int32_t val, u_int8_t *bytes)
bytes[3] = val & 0xff;
}
+static __inline void
+scsi_u64to8b(u_int64_t val, u_int8_t *bytes)
+{
+
+ bytes[0] = (val >> 56) & 0xff;
+ bytes[1] = (val >> 48) & 0xff;
+ bytes[2] = (val >> 40) & 0xff;
+ bytes[3] = (val >> 32) & 0xff;
+ bytes[4] = (val >> 24) & 0xff;
+ bytes[5] = (val >> 16) & 0xff;
+ bytes[6] = (val >> 8) & 0xff;
+ bytes[7] = val & 0xff;
+}
+
static __inline u_int32_t
scsi_2btoul(u_int8_t *bytes)
{
@@ -1103,6 +1163,22 @@ scsi_4btoul(u_int8_t *bytes)
return (rv);
}
+static __inline uint64_t
+scsi_8btou64(uint8_t *bytes)
+{
+ uint64_t rv;
+
+ rv = (((uint64_t)bytes[0]) << 56) |
+ (((uint64_t)bytes[1]) << 48) |
+ (((uint64_t)bytes[2]) << 40) |
+ (((uint64_t)bytes[3]) << 32) |
+ (((uint64_t)bytes[4]) << 24) |
+ (((uint64_t)bytes[5]) << 16) |
+ (((uint64_t)bytes[6]) << 8) |
+ bytes[7];
+ return (rv);
+}
+
/*
* Given the pointer to a returned mode sense buffer, return a pointer to
* the start of the first mode page.
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 1afd594..c8f930f 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -73,6 +73,7 @@
#ifdef _KERNEL
typedef enum {
DA_STATE_PROBE,
+ DA_STATE_PROBE2,
DA_STATE_NORMAL
} da_state;
@@ -96,9 +97,10 @@ typedef enum {
typedef enum {
DA_CCB_PROBE = 0x01,
- DA_CCB_BUFFER_IO = 0x02,
- DA_CCB_WAITING = 0x03,
- DA_CCB_DUMP = 0x04,
+ DA_CCB_PROBE2 = 0x02,
+ DA_CCB_BUFFER_IO = 0x03,
+ DA_CCB_WAITING = 0x04,
+ DA_CCB_DUMP = 0x05,
DA_CCB_TYPE_MASK = 0x0F,
DA_CCB_RETRY_UA = 0x10
} da_ccb_state;
@@ -109,10 +111,10 @@ typedef enum {
struct disk_params {
u_int8_t heads;
- u_int16_t cylinders;
+ u_int32_t cylinders;
u_int8_t secs_per_track;
u_int32_t secsize; /* Number of bytes/sector */
- u_int32_t sectors; /* total number sectors */
+ u_int64_t sectors; /* total number sectors */
};
struct da_softc {
@@ -505,6 +507,7 @@ static dumper_t dadump;
static periph_init_t dainit;
static void daasync(void *callback_arg, u_int32_t code,
struct cam_path *path, void *arg);
+static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS);
static periph_ctor_t daregister;
static periph_dtor_t dacleanup;
static periph_start_t dastart;
@@ -514,8 +517,9 @@ static void dadone(struct cam_periph *periph,
static int daerror(union ccb *ccb, u_int32_t cam_flags,
u_int32_t sense_flags);
static void daprevent(struct cam_periph *periph, int action);
-static void dasetgeom(struct cam_periph *periph,
- struct scsi_read_capacity_data * rdcap);
+static int dagetcapacity(struct cam_periph *periph);
+static void dasetgeom(struct cam_periph *periph, uint32_t block_len,
+ uint64_t maxsector);
static timeout_t dasendorderedtag;
static void dashutdown(void *arg, int howto);
@@ -570,8 +574,6 @@ daopen(struct disk *dp)
{
struct cam_periph *periph;
struct da_softc *softc;
- struct scsi_read_capacity_data *rcap;
- union ccb *ccb;
int unit;
int error;
int s;
@@ -603,38 +605,7 @@ daopen(struct disk *dp)
}
splx(s);
- /* Do a read capacity */
- rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
- M_TEMP,
- M_WAITOK);
-
- ccb = cam_periph_getccb(periph, /*priority*/1);
- scsi_read_capacity(&ccb->csio,
- /*retries*/4,
- /*cbfncp*/dadone,
- MSG_SIMPLE_Q_TAG,
- rcap,
- SSD_FULL_SIZE,
- /*timeout*/60000);
- ccb->ccb_h.ccb_bp = NULL;
-
- error = cam_periph_runccb(ccb, daerror,
- /*cam_flags*/CAM_RETRY_SELTO,
- /*sense_flags*/SF_RETRY_UA,
- softc->disk.d_devstat);
-
- if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
- cam_release_devq(ccb->ccb_h.path,
- /*relsim_flags*/0,
- /*reduction*/0,
- /*timeout*/0,
- /*getcount_only*/0);
- xpt_release_ccb(ccb);
-
- if (error == 0)
- dasetgeom(periph, rcap);
-
- free(rcap, M_TEMP);
+ error = dagetcapacity(periph);
if (error == 0) {
@@ -1075,18 +1046,18 @@ dacmdsizesysctl(SYSCTL_HANDLER_ARGS)
return (error);
/*
- * Acceptable values here are 6, 10 or 12. It's possible we may
- * support a 16 byte minimum command size in the future, since
- * there are now READ(16) and WRITE(16) commands defined in the
- * SBC-2 spec.
+ * Acceptable values here are 6, 10, 12 or 16.
*/
if (value < 6)
value = 6;
else if ((value > 6)
&& (value <= 10))
value = 10;
- else if (value > 10)
+ else if ((value > 10)
+ && (value <= 12))
value = 12;
+ else if (value > 12)
+ value = 16;
*(int *)arg1 = value;
@@ -1174,15 +1145,18 @@ daregister(struct cam_periph *periph, void *arg)
TUNABLE_INT_FETCH(tmpstr, &softc->minimum_cmd_size);
/*
- * 6, 10 and 12 are the currently permissible values.
+ * 6, 10, 12 and 16 are the currently permissible values.
*/
if (softc->minimum_cmd_size < 6)
softc->minimum_cmd_size = 6;
else if ((softc->minimum_cmd_size > 6)
&& (softc->minimum_cmd_size <= 10))
softc->minimum_cmd_size = 10;
- else if (softc->minimum_cmd_size > 12)
+ else if ((softc->minimum_cmd_size > 10)
+ && (softc->minimum_cmd_size <= 12))
softc->minimum_cmd_size = 12;
+ else if (softc->minimum_cmd_size > 12)
+ softc->minimum_cmd_size = 16;
/*
* Now register the sysctl handler, so the user can the value on
@@ -1285,17 +1259,18 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
}
scsi_read_write(&start_ccb->csio,
/*retries*/da_retry_count,
- dadone,
- tag_code,
- bp->bio_cmd == BIO_READ,
+ /*cbfcnp*/dadone,
+ /*tag_action*/tag_code,
+ /*read_op*/bp->bio_cmd == BIO_READ,
/*byte2*/0,
softc->minimum_cmd_size,
- bp->bio_pblkno,
- bp->bio_bcount / softc->params.secsize,
- bp->bio_data,
- bp->bio_bcount,
+ /*lba*/bp->bio_pblkno,
+ /*block_count*/bp->bio_bcount /
+ softc->params.secsize,
+ /*data_ptr*/ bp->bio_data,
+ /*dxfer_len*/ bp->bio_bcount,
/*sense_len*/SSD_FULL_SIZE,
- da_default_timeout * 1000);
+ /*timeout*/da_default_timeout*1000);
start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO;
/*
@@ -1353,6 +1328,34 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
xpt_action(start_ccb);
break;
}
+ case DA_STATE_PROBE2:
+ {
+ struct ccb_scsiio *csio;
+ struct scsi_read_capacity_data_long *rcaplong;
+
+ rcaplong = (struct scsi_read_capacity_data_long *)
+ malloc(sizeof(*rcaplong), M_TEMP, M_NOWAIT);
+ if (rcaplong == NULL) {
+ printf("dastart: Couldn't malloc read_capacity data\n");
+ /* da_free_periph??? */
+ break;
+ }
+ csio = &start_ccb->csio;
+ scsi_read_capacity_16(csio,
+ /*retries*/ 4,
+ /*cbfcnp*/ dadone,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*lba*/ 0,
+ /*reladr*/ 0,
+ /*pmi*/ 0,
+ rcaplong,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ 60000);
+ start_ccb->ccb_h.ccb_bp = NULL;
+ start_ccb->ccb_h.ccb_state = DA_CCB_PROBE2;
+ xpt_action(start_ccb);
+ break;
+ }
}
}
@@ -1510,20 +1513,53 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
break;
}
case DA_CCB_PROBE:
+ case DA_CCB_PROBE2:
{
struct scsi_read_capacity_data *rdcap;
+ struct scsi_read_capacity_data_long *rcaplong;
char announce_buf[80];
- rdcap = (struct scsi_read_capacity_data *)csio->data_ptr;
-
+ rdcap = NULL;
+ rcaplong = NULL;
+ if (softc->state == DA_STATE_PROBE)
+ rdcap =(struct scsi_read_capacity_data *)csio->data_ptr;
+ else
+ rcaplong = (struct scsi_read_capacity_data_long *)
+ csio->data_ptr;
+
if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
struct disk_params *dp;
+ uint32_t block_size;
+ uint64_t maxsector;
+
+ if (softc->state == DA_STATE_PROBE) {
+ block_size = scsi_4btoul(rdcap->length);
+ maxsector = scsi_4btoul(rdcap->addr);
- dasetgeom(periph, rdcap);
+ /*
+ * According to SBC-2, if the standard 10
+ * byte READ CAPACITY command returns 2^32,
+ * we should issue the 16 byte version of
+ * the command, since the device in question
+ * has more sectors than can be represented
+ * with the short version of the command.
+ */
+ if (maxsector == 0xffffffff) {
+ softc->state = DA_STATE_PROBE2;
+ free(rdcap, M_TEMP);
+ xpt_release_ccb(done_ccb);
+ cam_periph_unlock(periph);
+ break;
+ }
+ } else {
+ block_size = scsi_4btoul(rcaplong->length);
+ maxsector = scsi_8btou64(rcaplong->addr);
+ }
+ dasetgeom(periph, block_size, maxsector);
dp = &softc->params;
snprintf(announce_buf, sizeof(announce_buf),
- "%luMB (%u %u byte sectors: %dH %dS/T %dC)",
- (unsigned long) (((u_int64_t)dp->secsize *
+ "%quMB (%qu %u byte sectors: %dH %dS/T %dC)",
+ (uint64_t) (((u_int64_t)dp->secsize *
dp->sectors) / (1024*1024)), dp->sectors,
dp->secsize, dp->heads, dp->secs_per_track,
dp->cylinders);
@@ -1623,7 +1659,7 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
}
}
}
- free(rdcap, M_TEMP);
+ free(csio->data_ptr, M_TEMP);
if (announce_buf[0] != '\0')
xpt_announce_periph(periph, announce_buf);
softc->state = DA_STATE_NORMAL;
@@ -1736,8 +1772,101 @@ daprevent(struct cam_periph *periph, int action)
xpt_release_ccb(ccb);
}
+static int
+dagetcapacity(struct cam_periph *periph)
+{
+ struct da_softc *softc;
+ union ccb *ccb;
+ struct scsi_read_capacity_data *rcap;
+ struct scsi_read_capacity_data_long *rcaplong;
+ uint32_t block_len;
+ uint64_t maxsector;
+ int error;
+
+ softc = (struct da_softc *)periph->softc;
+ error = 0;
+
+ /* Do a read capacity */
+ rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcaplong),
+ M_TEMP,
+ M_WAITOK);
+
+ ccb = cam_periph_getccb(periph, /*priority*/1);
+ scsi_read_capacity(&ccb->csio,
+ /*retries*/4,
+ /*cbfncp*/dadone,
+ MSG_SIMPLE_Q_TAG,
+ rcap,
+ SSD_FULL_SIZE,
+ /*timeout*/60000);
+ ccb->ccb_h.ccb_bp = NULL;
+
+ error = cam_periph_runccb(ccb, daerror,
+ /*cam_flags*/CAM_RETRY_SELTO,
+ /*sense_flags*/SF_RETRY_UA,
+ softc->disk.d_devstat);
+
+ if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(ccb->ccb_h.path,
+ /*relsim_flags*/0,
+ /*reduction*/0,
+ /*timeout*/0,
+ /*getcount_only*/0);
+
+ if (error == 0) {
+ block_len = scsi_4btoul(rcap->length);
+ maxsector = scsi_4btoul(rcap->addr);
+
+ if (maxsector != 0xffffffff)
+ goto done;
+ } else
+ goto done;
+
+ rcaplong = (struct scsi_read_capacity_data_long *)rcap;
+
+ scsi_read_capacity_16(&ccb->csio,
+ /*retries*/ 4,
+ /*cbfcnp*/ dadone,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*lba*/ 0,
+ /*reladr*/ 0,
+ /*pmi*/ 0,
+ rcaplong,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ 60000);
+ ccb->ccb_h.ccb_bp = NULL;
+
+ error = cam_periph_runccb(ccb, daerror,
+ /*cam_flags*/CAM_RETRY_SELTO,
+ /*sense_flags*/SF_RETRY_UA,
+ softc->disk.d_devstat);
+
+ if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(ccb->ccb_h.path,
+ /*relsim_flags*/0,
+ /*reduction*/0,
+ /*timeout*/0,
+ /*getcount_only*/0);
+
+ if (error == 0) {
+ block_len = scsi_4btoul(rcaplong->length);
+ maxsector = scsi_8btou64(rcaplong->addr);
+ }
+
+done:
+
+ if (error == 0)
+ dasetgeom(periph, block_len, maxsector);
+
+ xpt_release_ccb(ccb);
+
+ free(rcap, M_TEMP);
+
+ return (error);
+}
+
static void
-dasetgeom(struct cam_periph *periph, struct scsi_read_capacity_data * rdcap)
+dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector)
{
struct ccb_calc_geometry ccg;
struct da_softc *softc;
@@ -1746,8 +1875,8 @@ dasetgeom(struct cam_periph *periph, struct scsi_read_capacity_data * rdcap)
softc = (struct da_softc *)periph->softc;
dp = &softc->params;
- dp->secsize = scsi_4btoul(rdcap->length);
- dp->sectors = scsi_4btoul(rdcap->addr) + 1;
+ dp->secsize = block_len;
+ dp->sectors = maxsector + 1;
/*
* Have the controller provide us with a geometry
* for this disk. The only time the geometry
OpenPOWER on IntegriCloud