Author: ken
Date: Thu Jan 26 18:09:28 2012
New Revision: 230590
URL: http://svn.freebsd.org/changeset/base/230590

Log:
  Add CAM infrastructure to allow reporting when a drive's long read capacity
  data changes.
  
  cam_ccb.h:    Add a new advanced information type, CDAI_TYPE_RCAPLONG,
                for long read capacity data.
  
  cam_xpt_internal.h:
                Add a read capacity data pointer and length to struct cam_ed.
  
  cam_xpt.c:    Free the read capacity buffer when a device goes away.
                While we're here, make sure we don't leak memory for other
                malloced fields in struct cam_ed.
  
  scsi_all.c:   Update the scsi_read_capacity_16() to take a uint8_t * and
                a length instead of just a pointer to the parameter data
                structure.  This will hopefully make this function somewhat
                immune to future changes in the parameter data.
  
  scsi_all.h:   Add some extra bit definitions to struct
                scsi_read_capacity_data_long, and bump up the structure
                size to the full size specified by SBC-3.
  
                Change the prototype for scsi_read_capacity_16().
  
  scsi_da.c:    Register changes in read capacity data with the transport
                layer.  This allows the transport layer to send out an
                async notification to interested parties.  Update the
                dasetgeom() API.
  
                Use scsi_extract_sense_len() instead of
                scsi_extract_sense().
  
  scsi_xpt.c:   Add support for the new CDAI_TYPE_RCAPLONG advanced
                information type.
  
                Make sure we set the physpath pointer to NULL after freeing
                it.  This allows blindly freeing it in the struct cam_ed
                destructor.
  
  sys/param.h:  Bump __FreeBSD_version from 1000005 to 1000006 to make it
                easier for third party drivers to determine that the read
                capacity data async notification is available.
  
  camcontrol.c,
  mptutil/mpt_cam.c:
                Update these for the new scsi_read_capacity_16() argument
                structure.
  
  Sponsored by: Spectra Logic

Modified:
  head/sbin/camcontrol/camcontrol.c
  head/sys/cam/cam_ccb.h
  head/sys/cam/cam_xpt.c
  head/sys/cam/cam_xpt_internal.h
  head/sys/cam/scsi/scsi_all.c
  head/sys/cam/scsi/scsi_all.h
  head/sys/cam/scsi/scsi_da.c
  head/sys/cam/scsi/scsi_xpt.c
  head/sys/sys/param.h
  head/usr.sbin/mptutil/mpt_cam.c

Modified: head/sbin/camcontrol/camcontrol.c
==============================================================================
--- head/sbin/camcontrol/camcontrol.c   Thu Jan 26 17:28:33 2012        
(r230589)
+++ head/sbin/camcontrol/camcontrol.c   Thu Jan 26 18:09:28 2012        
(r230590)
@@ -4232,7 +4232,8 @@ scsireadcapacity(struct cam_device *devi
                              /*lba*/ 0,
                              /*reladdr*/ 0,
                              /*pmi*/ 0,
-                             &rcaplong,
+                             /*rcap_buf*/ (uint8_t *)&rcaplong,
+                             /*rcap_buf_len*/ sizeof(rcaplong),
                              /*sense_len*/ SSD_FULL_SIZE,
                              /*timeout*/ timeout ? timeout : 5000);
 

Modified: head/sys/cam/cam_ccb.h
==============================================================================
--- head/sys/cam/cam_ccb.h      Thu Jan 26 17:28:33 2012        (r230589)
+++ head/sys/cam/cam_ccb.h      Thu Jan 26 18:09:28 2012        (r230590)
@@ -1118,6 +1118,7 @@ struct ccb_dev_advinfo {
 #define        CDAI_TYPE_SCSI_DEVID    1
 #define        CDAI_TYPE_SERIAL_NUM    2
 #define        CDAI_TYPE_PHYS_PATH     3
+#define        CDAI_TYPE_RCAPLONG      4
        off_t bufsiz;                   /* IN: Size of external buffer */
 #define        CAM_SCSI_DEVID_MAXLEN   65536   /* length in buffer is an 
uint16_t */
        off_t provsiz;                  /* OUT: Size required/used */

Modified: head/sys/cam/cam_xpt.c
==============================================================================
--- head/sys/cam/cam_xpt.c      Thu Jan 26 17:28:33 2012        (r230589)
+++ head/sys/cam/cam_xpt.c      Thu Jan 26 18:09:28 2012        (r230590)
@@ -4588,6 +4588,17 @@ xpt_release_device(struct cam_ed *device
                cam_devq_resize(devq, devq->alloc_queue.array_size - 1);
                camq_fini(&device->drvq);
                cam_ccbq_fini(&device->ccbq);
+               /*
+                * Free allocated memory.  free(9) does nothing if the
+                * supplied pointer is NULL, so it is safe to call without
+                * checking.
+                */
+               free(device->supported_vpds, M_CAMXPT);
+               free(device->device_id, M_CAMXPT);
+               free(device->physpath, M_CAMXPT);
+               free(device->rcap_buf, M_CAMXPT);
+               free(device->serial_num, M_CAMXPT);
+
                xpt_release_target(device->target);
                free(device, M_CAMXPT);
        } else

Modified: head/sys/cam/cam_xpt_internal.h
==============================================================================
--- head/sys/cam/cam_xpt_internal.h     Thu Jan 26 17:28:33 2012        
(r230589)
+++ head/sys/cam/cam_xpt_internal.h     Thu Jan 26 18:09:28 2012        
(r230590)
@@ -99,6 +99,8 @@ struct cam_ed {
        uint8_t          *device_id;
        uint8_t          physpath_len;
        uint8_t          *physpath;     /* physical path string form */
+       uint32_t         rcap_len;
+       uint8_t          *rcap_buf;
        struct           ata_params ident_data;
        u_int8_t         inq_flags;     /*
                                         * Current settings for inquiry flags.

Modified: head/sys/cam/scsi/scsi_all.c
==============================================================================
--- head/sys/cam/scsi/scsi_all.c        Thu Jan 26 17:28:33 2012        
(r230589)
+++ head/sys/cam/scsi/scsi_all.c        Thu Jan 26 18:09:28 2012        
(r230590)
@@ -5325,8 +5325,8 @@ 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)
+                     uint8_t *rcap_buf, int rcap_buf_len, uint8_t sense_len,
+                     uint32_t timeout)
 {
        struct scsi_read_capacity_16 *scsi_cmd;
 
@@ -5337,7 +5337,7 @@ scsi_read_capacity_16(struct ccb_scsiio 
                      /*flags*/CAM_DIR_IN,
                      tag_action,
                      /*data_ptr*/(u_int8_t *)rcap_buf,
-                     /*dxfer_len*/sizeof(*rcap_buf),
+                     /*dxfer_len*/rcap_buf_len,
                      sense_len,
                      sizeof(*scsi_cmd),
                      timeout);
@@ -5346,7 +5346,7 @@ scsi_read_capacity_16(struct ccb_scsiio 
        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);
+       scsi_ulto4b(rcap_buf_len, scsi_cmd->alloc_len);
        if (pmi)
                reladr |= SRC16_PMI;
        if (reladr)

Modified: head/sys/cam/scsi/scsi_all.h
==============================================================================
--- head/sys/cam/scsi/scsi_all.h        Thu Jan 26 17:28:33 2012        
(r230589)
+++ head/sys/cam/scsi/scsi_all.h        Thu Jan 26 18:09:28 2012        
(r230590)
@@ -1437,15 +1437,26 @@ struct scsi_read_capacity_data_long
        uint8_t length[4];
 #define        SRC16_PROT_EN           0x01
 #define        SRC16_P_TYPE            0x0e
+#define        SRC16_PTYPE_1           0x00
+#define        SRC16_PTYPE_2           0x02
+#define        SRC16_PTYPE_3           0x04
        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
+#define        SRC16_LALBA             0x3f
+#define        SRC16_LBPRZ             0x40
+#define        SRC16_LBPME             0x80
+/*
+ * Alternate versions of these macros that are intended for use on a 16-bit
+ * version of the lalba_lbp field instead of the array of 2 8 bit numbers.
+ */
+#define        SRC16_LALBA_A           0x3fff
+#define        SRC16_LBPRZ_A           0x4000
+#define        SRC16_LBPME_A           0x8000
        uint8_t lalba_lbp[2];
+       uint8_t reserved[16];
 };
 
 struct scsi_report_luns
@@ -2293,9 +2304,8 @@ void              scsi_read_capacity_16(struct ccb_s
                                      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);
+                                     uint8_t *rcap_buf, int rcap_buf_len,
+                                     uint8_t sense_len, uint32_t timeout);
 
 void           scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
                                 void (*cbfcnp)(struct cam_periph *, 

Modified: head/sys/cam/scsi/scsi_da.c
==============================================================================
--- head/sys/cam/scsi/scsi_da.c Thu Jan 26 17:28:33 2012        (r230589)
+++ head/sys/cam/scsi/scsi_da.c Thu Jan 26 18:09:28 2012        (r230590)
@@ -160,6 +160,7 @@ struct da_softc {
        struct callout          sendordered_c;
        uint64_t wwpn;
        uint8_t  unmap_buf[UNMAP_MAX_RANGES * 16 + 8];
+       struct scsi_read_capacity_data_long rcaplong;
 };
 
 struct da_quirk_entry {
@@ -830,7 +831,9 @@ static  int         daerror(union ccb *ccb, u_i
 static void            daprevent(struct cam_periph *periph, int action);
 static int             dagetcapacity(struct cam_periph *periph);
 static void            dasetgeom(struct cam_periph *periph, uint32_t block_len,
-                                 uint64_t maxsector, u_int lbppbe, u_int 
lalba);
+                                 uint64_t maxsector,
+                                 struct scsi_read_capacity_data_long *rcaplong,
+                                 size_t rcap_size);
 static timeout_t       dasendorderedtag;
 static void            dashutdown(void *arg, int howto);
 
@@ -1948,7 +1951,8 @@ out:
                                      /*lba*/ 0,
                                      /*reladr*/ 0,
                                      /*pmi*/ 0,
-                                     rcaplong,
+                                     /*rcap_buf*/ (uint8_t *)rcaplong,
+                                     /*rcap_buf_len*/ sizeof(*rcaplong),
                                      /*sense_len*/ SSD_FULL_SIZE,
                                      /*timeout*/ 60000);
                start_ccb->ccb_h.ccb_bp = NULL;
@@ -2227,10 +2231,15 @@ dadone(struct cam_periph *periph, union 
                                announce_buf[0] = '\0';
                                cam_periph_invalidate(periph);
                        } else {
+                               /*
+                                * We pass rcaplong into dasetgeom(),
+                                * because it will only use it if it is
+                                * non-NULL.
+                                */
                                dasetgeom(periph, block_size, maxsector,
-                                   lbppbe, lalba & SRC16_LALBA);
-                               if ((lalba & SRC16_LBPME) &&
-                                   softc->delete_method == DA_DELETE_NONE)
+                                         rcaplong, sizeof(*rcaplong));
+                               if ((lalba & SRC16_LBPME_A)
+                                && softc->delete_method == DA_DELETE_NONE)
                                        softc->delete_method = DA_DELETE_UNMAP;
                                dp = &softc->params;
                                snprintf(announce_buf, sizeof(announce_buf),
@@ -2504,6 +2513,7 @@ dagetcapacity(struct cam_periph *periph)
        lalba = 0;
        error = 0;
        rc16failed = 0;
+       rcaplong = NULL;
        sense_flags = SF_RETRY_UA;
        if (softc->flags & DA_FLAG_PACK_REMOVABLE)
                sense_flags |= SF_NO_PRINT;
@@ -2521,39 +2531,47 @@ dagetcapacity(struct cam_periph *periph)
        /* 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);
+                                     /*retries*/ 4,
+                                     /*cbfcnp*/ dadone,
+                                     /*tag_action*/ MSG_SIMPLE_Q_TAG,
+                                     /*lba*/ 0,
+                                     /*reladr*/ 0,
+                                     /*pmi*/ 0,
+                                     /*rcap_buf*/ (uint8_t *)rcaplong,
+                                     /*rcap_buf_len*/ sizeof(*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);
+                                         /*cam_flags*/CAM_RETRY_SELTO,
+                                         sense_flags, softc->disk->d_devstat);
                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) {
+               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)) {
+                            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)
+                       scsi_extract_sense_len(&ccb->csio.sense_data,
+                                              ccb->csio.sense_len -
+                                              ccb->csio.sense_resid,
+                                              &error_code, &sense_key,
+                                              &asc, &ascq, /*show_errors*/1);
+                       /*
+                        * If we don't have enough sense to get the sense
+                        * key, or if it's illegal request, turn off
+                        * READ CAPACITY (16).
+                        */
+                       if ((sense_key == -1)
+                        || (sense_key == SSD_KEY_ILLEGAL_REQUEST))
                                softc->flags &= ~DA_FLAG_CAN_RC16;
                }
                rc16failed = 1;
@@ -2590,7 +2608,8 @@ dagetcapacity(struct cam_periph *periph)
                              /*lba*/ 0,
                              /*reladr*/ 0,
                              /*pmi*/ 0,
-                             rcaplong,
+                             /*rcap_buf*/ (uint8_t *)rcaplong,
+                             /*rcap_buf_len*/ sizeof(*rcaplong),
                              /*sense_len*/ SSD_FULL_SIZE,
                              /*timeout*/ 60000);
        ccb->ccb_h.ccb_bp = NULL;
@@ -2617,9 +2636,9 @@ done:
                        error = EINVAL;
                } else {
                        dasetgeom(periph, block_len, maxsector,
-                           lbppbe, lalba & SRC16_LALBA);
-                       if ((lalba & SRC16_LBPME) &&
-                           softc->delete_method == DA_DELETE_NONE)
+                                 rcaplong, sizeof(*rcaplong));
+                       if ((lalba & SRC16_LBPME)
+                        && softc->delete_method == DA_DELETE_NONE)
                                softc->delete_method = DA_DELETE_UNMAP;
                }
        }
@@ -2633,17 +2652,27 @@ done:
 
 static void
 dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector,
-    u_int lbppbe, u_int lalba)
+         struct scsi_read_capacity_data_long *rcaplong, size_t rcap_len)
 {
        struct ccb_calc_geometry ccg;
        struct da_softc *softc;
        struct disk_params *dp;
+       u_int lbppbe, lalba;
 
        softc = (struct da_softc *)periph->softc;
 
        dp = &softc->params;
        dp->secsize = block_len;
        dp->sectors = maxsector + 1;
+       if (rcaplong != NULL) {
+               lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE;
+               lalba = scsi_2btoul(rcaplong->lalba_lbp);
+               lalba &= SRC16_LALBA_A;
+       } else {
+               lbppbe = 0;
+               lalba = 0;
+       }
+
        if (lbppbe > 0) {
                dp->stripesize = block_len << lbppbe;
                dp->stripeoffset = (dp->stripesize - block_len * lalba) %
@@ -2688,6 +2717,38 @@ dasetgeom(struct cam_periph *periph, uin
                dp->secs_per_track = ccg.secs_per_track;
                dp->cylinders = ccg.cylinders;
        }
+
+       /*
+        * If the user supplied a read capacity buffer, and if it is
+        * different than the previous buffer, update the data in the EDT.
+        * If it's the same, we don't bother.  This avoids sending an
+        * update every time someone opens this device.
+        */
+       if ((rcaplong != NULL)
+        && (bcmp(rcaplong, &softc->rcaplong,
+                 min(sizeof(softc->rcaplong), rcap_len)) != 0)) {
+               struct ccb_dev_advinfo cdai;
+
+               xpt_setup_ccb(&cdai.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
+               cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
+               cdai.buftype = CDAI_TYPE_RCAPLONG;
+               cdai.flags |= CDAI_FLAG_STORE;
+               cdai.bufsiz = rcap_len;
+               cdai.buf = (uint8_t *)rcaplong;
+               xpt_action((union ccb *)&cdai);
+               if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
+                       cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
+               if (cdai.ccb_h.status != CAM_REQ_CMP) {
+                       xpt_print(periph->path, "%s: failed to set read "
+                                 "capacity advinfo\n", __func__);
+                       /* Use cam_error_print() to decode the status */
+                       cam_error_print((union ccb *)&cdai, CAM_ESF_CAM_STATUS,
+                                       CAM_EPF_ALL);
+               } else {
+                       bcopy(rcaplong, &softc->rcaplong,
+                             min(sizeof(softc->rcaplong), rcap_len));
+               }
+       }
 }
 
 static void

Modified: head/sys/cam/scsi/scsi_xpt.c
==============================================================================
--- head/sys/cam/scsi/scsi_xpt.c        Thu Jan 26 17:28:33 2012        
(r230589)
+++ head/sys/cam/scsi/scsi_xpt.c        Thu Jan 26 18:09:28 2012        
(r230590)
@@ -2468,8 +2468,10 @@ scsi_dev_advinfo(union ccb *start_ccb)
                break;
        case CDAI_TYPE_PHYS_PATH:
                if (cdai->flags & CDAI_FLAG_STORE) {
-                       if (device->physpath != NULL)
+                       if (device->physpath != NULL) {
                                free(device->physpath, M_CAMXPT);
+                               device->physpath = NULL;
+                       }
                        device->physpath_len = cdai->bufsiz;
                        /* Clear existing buffer if zero length */
                        if (cdai->bufsiz == 0)
@@ -2490,6 +2492,36 @@ scsi_dev_advinfo(union ccb *start_ccb)
                        memcpy(cdai->buf, device->physpath, amt);
                }
                break;
+       case CDAI_TYPE_RCAPLONG:
+               if (cdai->flags & CDAI_FLAG_STORE) {
+                       if (device->rcap_buf != NULL) {
+                               free(device->rcap_buf, M_CAMXPT);
+                               device->rcap_buf = NULL;
+                       }
+
+                       device->rcap_len = cdai->bufsiz;
+                       /* Clear existing buffer if zero length */
+                       if (cdai->bufsiz == 0)
+                               break;
+
+                       device->rcap_buf = malloc(cdai->bufsiz, M_CAMXPT,
+                                                 M_NOWAIT);
+                       if (device->rcap_buf == NULL) {
+                               start_ccb->ccb_h.status = CAM_REQ_ABORTED;
+                               return;
+                       }
+
+                       memcpy(device->rcap_buf, cdai->buf, cdai->bufsiz);
+               } else {
+                       cdai->provsiz = device->rcap_len;
+                       if (device->rcap_len == 0)
+                               break;
+                       amt = device->rcap_len;
+                       if (cdai->provsiz > cdai->bufsiz)
+                               amt = cdai->bufsiz;
+                       memcpy(cdai->buf, device->rcap_buf, amt);
+               }
+               break;
        default:
                return;
        }

Modified: head/sys/sys/param.h
==============================================================================
--- head/sys/sys/param.h        Thu Jan 26 17:28:33 2012        (r230589)
+++ head/sys/sys/param.h        Thu Jan 26 18:09:28 2012        (r230590)
@@ -58,7 +58,7 @@
  *             in the range 5 to 9.
  */
 #undef __FreeBSD_version
-#define __FreeBSD_version 1000005      /* Master, propagated to newvers */
+#define __FreeBSD_version 1000006      /* Master, propagated to newvers */
 
 /*
  * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,

Modified: head/usr.sbin/mptutil/mpt_cam.c
==============================================================================
--- head/usr.sbin/mptutil/mpt_cam.c     Thu Jan 26 17:28:33 2012        
(r230589)
+++ head/usr.sbin/mptutil/mpt_cam.c     Thu Jan 26 18:09:28 2012        
(r230590)
@@ -277,7 +277,7 @@ fetch_scsi_capacity(struct cam_device *d
            sizeof(struct ccb_hdr));
 
        scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
-           &rcaplong, SSD_FULL_SIZE, 5000);
+           (uint8_t *)&rcaplong, sizeof(rcaplong), SSD_FULL_SIZE, 5000);
 
        /* Disable freezing the device queue */
        ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
_______________________________________________
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