Author: mav
Date: Fri Dec 23 19:12:02 2011
New Revision: 228846
URL: http://svn.freebsd.org/changeset/base/228846

Log:
  Use READ CAPACITY(16) to get information about device physical sectors.
  As soon as not all devices support READ CAPACITY(16), automatically fall
  back to READ CAPACITY(10) if CAM_REQ_INVALID or SSD_KEY_ILLEGAL_REQUEST
  status returned.
  
  It also provides first bits of information about Logical Block Provisioning
  (aka UNMAP/TRIM) support by the device.

Modified:
  head/sys/cam/scsi/scsi_all.h
  head/sys/cam/scsi/scsi_da.c

Modified: head/sys/cam/scsi/scsi_all.h
==============================================================================
--- head/sys/cam/scsi/scsi_all.h        Fri Dec 23 16:03:57 2011        
(r228845)
+++ head/sys/cam/scsi/scsi_all.h        Fri Dec 23 19:12:02 2011        
(r228846)
@@ -1349,6 +1349,17 @@ struct scsi_read_capacity_data_long
 {
        uint8_t addr[8];
        uint8_t length[4];
+#define        SRC16_PROT_EN           0x01
+#define        SRC16_P_TYPE            0x0e
+       uint8_t prot;
+#define        SRC16_LBPPBE            0x0f
+#define        SRC16_PI_EXPONENT       0xf0
+#define        SRC16_PI_EXPONENT_SHIFT 4
+       uint8_t prot_lbppbe;
+#define        SRC16_LALBA             0x3fff
+#define        SRC16_LBPRZ             0x4000
+#define        SRC16_LBPME             0x8000
+       uint8_t lalba_lbp[2];
 };
 
 struct scsi_report_luns

Modified: head/sys/cam/scsi/scsi_da.c
==============================================================================
--- head/sys/cam/scsi/scsi_da.c Fri Dec 23 16:03:57 2011        (r228845)
+++ head/sys/cam/scsi/scsi_da.c Fri Dec 23 19:12:02 2011        (r228846)
@@ -82,7 +82,9 @@ typedef enum {
        DA_FLAG_WENT_IDLE       = 0x040,
        DA_FLAG_RETRY_UA        = 0x080,
        DA_FLAG_OPEN            = 0x100,
-       DA_FLAG_SCTX_INIT       = 0x200
+       DA_FLAG_SCTX_INIT       = 0x200,
+       DA_FLAG_CAN_RC16        = 0x400,
+       DA_FLAG_CAN_LBPME       = 0x800
 } da_flags;
 
 typedef enum {
@@ -1533,6 +1535,14 @@ daregister(struct cam_periph *periph, vo
        else if (softc->minimum_cmd_size > 12)
                softc->minimum_cmd_size = 16;
 
+       /* Predict whether device may support READ CAPACITY(16). */
+       if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3 ||
+           (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC &&
+            (cgd->inq_data.spc3_flags & SPC3_SID_PROTECT))) {
+               softc->flags |= DA_FLAG_CAN_RC16;
+               softc->state = DA_STATE_PROBE2;
+       }
+
        /*
         * Register this media as a disk.
         */
@@ -1922,10 +1932,14 @@ dadone(struct cam_periph *periph, union 
                        struct disk_params *dp;
                        uint32_t block_size;
                        uint64_t maxsector;
+                       u_int lbppbe;   /* LB per physical block exponent. */
+                       u_int lalba;    /* Lowest aligned LBA. */
 
                        if (softc->state == DA_STATE_PROBE) {
                                block_size = scsi_4btoul(rdcap->length);
                                maxsector = scsi_4btoul(rdcap->addr);
+                               lbppbe = 0;
+                               lalba = 0;
 
                                /*
                                 * According to SBC-2, if the standard 10
@@ -1945,6 +1959,8 @@ dadone(struct cam_periph *periph, union 
                        } else {
                                block_size = scsi_4btoul(rcaplong->length);
                                maxsector = scsi_8btou64(rcaplong->addr);
+                               lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE;
+                               lalba = scsi_2btoul(rcaplong->lalba_lbp);
                        }
 
                        /*
@@ -1962,7 +1978,12 @@ dadone(struct cam_periph *periph, union 
                                announce_buf[0] = '\0';
                                cam_periph_invalidate(periph);
                        } else {
-                               dasetgeom(periph, block_size, maxsector, 0, 0);
+                               dasetgeom(periph, block_size, maxsector,
+                                   lbppbe, lalba & SRC16_LALBA);
+                               if (lalba & SRC16_LBPME)
+                                       softc->flags |= DA_FLAG_CAN_LBPME;
+                               else
+                                       softc->flags &= ~DA_FLAG_CAN_LBPME;
                                dp = &softc->params;
                                snprintf(announce_buf, sizeof(announce_buf),
                                        "%juMB (%ju %u byte sectors: %dH %dS/T "
@@ -2029,6 +2050,24 @@ dadone(struct cam_periph *periph, union 
                                            &ascq, /*show_errors*/ 1);
                                }
                                /*
+                                * If we tried READ CAPACITY(16) and failed,
+                                * fallback to READ CAPACITY(10).
+                                */
+                               if ((softc->state == DA_STATE_PROBE2) &&
+                                   (softc->flags & DA_FLAG_CAN_RC16) &&
+                                   (((csio->ccb_h.status & CAM_STATUS_MASK) ==
+                                       CAM_REQ_INVALID) ||
+                                    ((have_sense) &&
+                                     (error_code == SSD_CURRENT_ERROR) &&
+                                     (sense_key == SSD_KEY_ILLEGAL_REQUEST)))) 
{
+                                       softc->flags &= ~DA_FLAG_CAN_RC16;
+                                       softc->state = DA_STATE_PROBE;
+                                       free(rdcap, M_SCSIDA);
+                                       xpt_release_ccb(done_ccb);
+                                       xpt_schedule(periph, priority);
+                                       return;
+                               } else
+                               /*
                                 * Attach to anything that claims to be a
                                 * direct access or optical disk device,
                                 * as long as it doesn't return a "Logical
@@ -2198,13 +2237,18 @@ dagetcapacity(struct cam_periph *periph)
        struct scsi_read_capacity_data_long *rcaplong;
        uint32_t block_len;
        uint64_t maxsector;
-       int error;
+       int error, rc16failed;
        u_int32_t sense_flags;
+       u_int lbppbe;   /* Logical blocks per physical block exponent. */
+       u_int lalba;    /* Lowest aligned LBA. */
 
        softc = (struct da_softc *)periph->softc;
        block_len = 0;
        maxsector = 0;
+       lbppbe = 0;
+       lalba = 0;
        error = 0;
+       rc16failed = 0;
        sense_flags = SF_RETRY_UA;
        if (softc->flags & DA_FLAG_PACK_REMOVABLE)
                sense_flags |= SF_NO_PRINT;
@@ -2212,11 +2256,63 @@ dagetcapacity(struct cam_periph *periph)
        /* Do a read capacity */
        rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcaplong),
                                                        M_SCSIDA,
-                                                       M_NOWAIT);
+                                                       M_NOWAIT | M_ZERO);
        if (rcap == NULL)
                return (ENOMEM);
-               
+       rcaplong = (struct scsi_read_capacity_data_long *)rcap;
+
        ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+
+       /* Try READ CAPACITY(16) first if we think it should work. */
+       if (softc->flags & DA_FLAG_CAN_RC16) {
+               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,
+                                 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)
+                       goto rc16ok;
+
+               /* If we got ILLEGAL REQUEST, do not prefer RC16 any more. */
+               if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+                    CAM_REQ_INVALID) {
+                       softc->flags &= ~DA_FLAG_CAN_RC16;
+               } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+                    CAM_SCSI_STATUS_ERROR) &&
+                   (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) &&
+                   (ccb->ccb_h.status & CAM_AUTOSNS_VALID) &&
+                   ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) &&
+                   ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) {
+                       int sense_key, error_code, asc, ascq;
+
+                       scsi_extract_sense(&ccb->csio.sense_data,
+                                  &error_code, &sense_key, &asc, &ascq);
+                       if (sense_key == SSD_KEY_ILLEGAL_REQUEST)
+                               softc->flags &= ~DA_FLAG_CAN_RC16;
+               }
+               rc16failed = 1;
+       }
+
+       /* Do READ CAPACITY(10). */
        scsi_read_capacity(&ccb->csio,
                           /*retries*/4,
                           /*cbfncp*/dadone,
@@ -2242,13 +2338,12 @@ dagetcapacity(struct cam_periph *periph)
                block_len = scsi_4btoul(rcap->length);
                maxsector = scsi_4btoul(rcap->addr);
 
-               if (maxsector != 0xffffffff)
+               if (maxsector != 0xffffffff || rc16failed)
                        goto done;
        } else
                goto done;
 
-       rcaplong = (struct scsi_read_capacity_data_long *)rcap;
-
+       /* If READ CAPACITY(10) returned overflow, use READ CAPACITY(16) */
        scsi_read_capacity_16(&ccb->csio,
                              /*retries*/ 4,
                              /*cbfcnp*/ dadone,
@@ -2274,8 +2369,11 @@ dagetcapacity(struct cam_periph *periph)
                                 /*getcount_only*/0);
 
        if (error == 0) {
+rc16ok:
                block_len = scsi_4btoul(rcaplong->length);
                maxsector = scsi_8btou64(rcaplong->addr);
+               lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE;
+               lalba = scsi_2btoul(rcaplong->lalba_lbp);
        }
 
 done:
@@ -2286,8 +2384,14 @@ done:
                            "unsupportable block size %ju\n",
                            (uintmax_t) block_len);
                        error = EINVAL;
-               } else
-                       dasetgeom(periph, block_len, maxsector, 0, 0);
+               } else {
+                       dasetgeom(periph, block_len, maxsector,
+                           lbppbe, lalba & SRC16_LALBA);
+                       if (lalba & SRC16_LBPME)
+                               softc->flags |= DA_FLAG_CAN_LBPME;
+                       else
+                               softc->flags &= ~DA_FLAG_CAN_LBPME;
+               }
        }
 
        xpt_release_ccb(ccb);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to