Author: smh
Date: Fri Apr 26 16:22:54 2013
New Revision: 249940
URL: http://svnweb.freebsd.org/changeset/base/249940

Log:
  Teach GEOM and CAM about the difference between the max "size" of r/w and 
delete
  requests.
  
  sys/geom/geom_disk.h:
          - Added d_delmaxsize which represents the maximum size of individual
            device delete requests in bytes. This can be used by devices to
            inform geom of their size limitations regarding delete operations
            which are generally different from the read / write limits as data
            is not usually transferred from the host to physical device.
  
  sys/geom/geom_disk.c:
          - Use new d_delmaxsize to calculate the size of chunks passed through 
to
            the underlying strategy during deletes instead of using read / write
            optimised values. This defaults to d_maxsize if unset (0).
  
          - Moved d_maxsize default up so it can be used to default d_delmaxsize
  
  sys/cam/ata/ata_da.c:
          - Added d_delmaxsize calculations for TRIM and CFA
  
  sys/cam/scsi/scsi_da.c:
          - Added re-calculation of d_delmaxsize whenever delete_method is set.
  
          - Added kern.cam.da.X.delete_max sysctl which allows the max size for
            delete requests to be limited. This is useful in preventing timeouts
            on devices who's delete methods are slow. It should be noted that
            this limit is reset then the device delete method is changed and
            that it can only be lowered not increased from the device max.
  
  Reviewed by:  mav
  Approved by:  pjd (mentor)

Modified:
  head/sys/cam/ata/ata_da.c
  head/sys/cam/scsi/scsi_da.c
  head/sys/geom/geom_disk.c
  head/sys/geom/geom_disk.h

Modified: head/sys/cam/ata/ata_da.c
==============================================================================
--- head/sys/cam/ata/ata_da.c   Fri Apr 26 16:17:04 2013        (r249939)
+++ head/sys/cam/ata/ata_da.c   Fri Apr 26 16:22:54 2013        (r249940)
@@ -1188,10 +1188,15 @@ adaregister(struct cam_periph *periph, v
                softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE;
        if (softc->flags & ADA_FLAG_CAN_TRIM) {
                softc->disk->d_flags |= DISKFLAG_CANDELETE;
+               softc->disk->d_delmaxsize = softc->params.secsize *
+                                           ATA_DSM_RANGE_MAX *
+                                           softc->trim_max_ranges;
        } else if ((softc->flags & ADA_FLAG_CAN_CFA) &&
            !(softc->flags & ADA_FLAG_CAN_48BIT)) {
                softc->disk->d_flags |= DISKFLAG_CANDELETE;
-       }
+               softc->disk->d_delmaxsize = 256 * softc->params.secsize;
+       } else
+               softc->disk->d_delmaxsize = maxio;
        if ((cpi.hba_misc & PIM_UNMAPPED) != 0)
                softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO;
        strlcpy(softc->disk->d_descr, cgd->ident_data.model,

Modified: head/sys/cam/scsi/scsi_da.c
==============================================================================
--- head/sys/cam/scsi/scsi_da.c Fri Apr 26 16:17:04 2013        (r249939)
+++ head/sys/cam/scsi/scsi_da.c Fri Apr 26 16:22:54 2013        (r249940)
@@ -909,8 +909,11 @@ static     void            daasync(void *callback_arg,
 static void            dasysctlinit(void *context, int pending);
 static int             dacmdsizesysctl(SYSCTL_HANDLER_ARGS);
 static int             dadeletemethodsysctl(SYSCTL_HANDLER_ARGS);
+static int             dadeletemaxsysctl(SYSCTL_HANDLER_ARGS);
 static void            dadeletemethodset(struct da_softc *softc,
                                          da_delete_methods delete_method);
+static off_t           dadeletemaxsize(struct da_softc *softc,
+                                       da_delete_methods delete_method);
 static void            dadeletemethodchoose(struct da_softc *softc,
                                             da_delete_methods default_method);
 
@@ -1536,6 +1539,10 @@ dasysctlinit(void *context, int pending)
                softc, 0, dadeletemethodsysctl, "A",
                "BIO_DELETE execution method");
        SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
+               OID_AUTO, "delete_max", CTLTYPE_U64 | CTLFLAG_RW,
+               softc, 0, dadeletemaxsysctl, "Q",
+               "Maximum BIO_DELETE size");
+       SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
                OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW,
                &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I",
                "Minimum CDB size");
@@ -1581,6 +1588,29 @@ dasysctlinit(void *context, int pending)
 }
 
 static int
+dadeletemaxsysctl(SYSCTL_HANDLER_ARGS)
+{
+       int error;
+       uint64_t value;
+       struct da_softc *softc;
+
+       softc = (struct da_softc *)arg1;
+
+       value = softc->disk->d_delmaxsize;
+       error = sysctl_handle_64(oidp, &value, 0, req);
+       if ((error != 0) || (req->newptr == NULL))
+               return (error);
+
+       /* only accept values smaller than the calculated value */
+       if (value > softc->disk->d_delmaxsize) {
+               return (EINVAL);
+       }
+       softc->disk->d_delmaxsize = value;
+
+       return (0);
+}
+
+static int
 dacmdsizesysctl(SYSCTL_HANDLER_ARGS)
 {
        int error, value;
@@ -1618,6 +1648,7 @@ dadeletemethodset(struct da_softc *softc
 
 
        softc->delete_method = delete_method;
+       softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method);
 
        if (softc->delete_method > DA_DELETE_DISABLE)
                softc->disk->d_flags |= DISKFLAG_CANDELETE;
@@ -1625,6 +1656,33 @@ dadeletemethodset(struct da_softc *softc
                softc->disk->d_flags &= ~DISKFLAG_CANDELETE;
 }
 
+static off_t
+dadeletemaxsize(struct da_softc *softc, da_delete_methods delete_method)
+{
+       off_t sectors;
+
+       switch(delete_method) {
+       case DA_DELETE_UNMAP:
+               sectors = (off_t)softc->unmap_max_lba * softc->unmap_max_ranges;
+               break;
+       case DA_DELETE_ATA_TRIM:
+               sectors = (off_t)ATA_DSM_RANGE_MAX * softc->trim_max_ranges;
+               break;
+       case DA_DELETE_WS16:
+               sectors = (off_t)min(softc->ws_max_blks, WS16_MAX_BLKS);
+               break;
+       case DA_DELETE_ZERO:
+       case DA_DELETE_WS10:
+               sectors = (off_t)min(softc->ws_max_blks, WS10_MAX_BLKS);
+               break;
+       default:
+               return 0;
+       }
+
+       return (off_t)softc->params.secsize *
+           min(sectors, (off_t)softc->params.sectors);
+}
+
 static void
 dadeletemethodchoose(struct da_softc *softc, da_delete_methods default_method)
 {
@@ -2088,8 +2146,13 @@ skipstate:
                    } else if (softc->delete_method == DA_DELETE_ZERO ||
                               softc->delete_method == DA_DELETE_WS10 ||
                               softc->delete_method == DA_DELETE_WS16) {
+                       /*
+                        * We calculate ws_max_blks here based off d_delmaxsize 
instead
+                        * of using softc->ws_max_blks as it is absolute max 
for the
+                        * device not the protocol max which may well be lower
+                        */
                        uint64_t ws_max_blks;
-                       ws_max_blks = softc->ws_max_blks / 
softc->params.secsize;
+                       ws_max_blks = softc->disk->d_delmaxsize / 
softc->params.secsize;
                        softc->delete_running = 1;
                        lba = bp->bio_pblkno;
                        count = 0;

Modified: head/sys/geom/geom_disk.c
==============================================================================
--- head/sys/geom/geom_disk.c   Fri Apr 26 16:17:04 2013        (r249939)
+++ head/sys/geom/geom_disk.c   Fri Apr 26 16:22:54 2013        (r249940)
@@ -141,14 +141,23 @@ g_disk_access(struct g_provider *pp, int
                }
                pp->mediasize = dp->d_mediasize;
                pp->sectorsize = dp->d_sectorsize;
-               pp->stripeoffset = dp->d_stripeoffset;
-               pp->stripesize = dp->d_stripesize;
-               dp->d_flags |= DISKFLAG_OPEN;
                if (dp->d_maxsize == 0) {
                        printf("WARNING: Disk drive %s%d has no d_maxsize\n",
                            dp->d_name, dp->d_unit);
                        dp->d_maxsize = DFLTPHYS;
                }
+               if (dp->d_flags & DISKFLAG_CANDELETE) {
+                       if (bootverbose && dp->d_delmaxsize == 0) {
+                               printf("WARNING: Disk drive %s%d has no 
d_delmaxsize\n",
+                                   dp->d_name, dp->d_unit);
+                               dp->d_delmaxsize = dp->d_maxsize;
+                       }
+               } else {
+                       dp->d_delmaxsize = 0;
+               }
+               pp->stripeoffset = dp->d_stripeoffset;
+               pp->stripesize = dp->d_stripesize;
+               dp->d_flags |= DISKFLAG_OPEN;
        } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) {
                if (dp->d_close != NULL) {
                        g_disk_lock_giant(dp);
@@ -293,6 +302,10 @@ g_disk_start(struct bio *bp)
                        break;
                }
                do {
+                       off_t d_maxsize;
+
+                       d_maxsize = (bp->bio_cmd == BIO_DELETE) ?
+                           dp->d_delmaxsize : dp->d_maxsize;
                        bp2->bio_offset += off;
                        bp2->bio_length -= off;
                        if ((bp->bio_flags & BIO_UNMAPPED) == 0) {
@@ -307,18 +320,20 @@ g_disk_start(struct bio *bp)
                                bp2->bio_ma_offset %= PAGE_SIZE;
                                bp2->bio_ma_n -= off / PAGE_SIZE;
                        }
-                       if (bp2->bio_length > dp->d_maxsize) {
+                       if (bp2->bio_length > d_maxsize) {
                                /*
                                 * XXX: If we have a stripesize we should really
-                                * use it here.
+                                * use it here. Care should be taken in the 
delete
+                                * case if this is done as deletes can be very 
+                                * sensitive to size given how they are 
processed.
                                 */
-                               bp2->bio_length = dp->d_maxsize;
+                               bp2->bio_length = d_maxsize;
                                if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
                                        bp2->bio_ma_n = howmany(
                                            bp2->bio_ma_offset +
                                            bp2->bio_length, PAGE_SIZE);
                                }
-                               off += dp->d_maxsize;
+                               off += d_maxsize;
                                /*
                                 * To avoid a race, we need to grab the next bio
                                 * before we schedule this one.  See "notes".

Modified: head/sys/geom/geom_disk.h
==============================================================================
--- head/sys/geom/geom_disk.h   Fri Apr 26 16:17:04 2013        (r249939)
+++ head/sys/geom/geom_disk.h   Fri Apr 26 16:22:54 2013        (r249940)
@@ -88,6 +88,7 @@ struct disk {
        u_int                   d_fwsectors;
        u_int                   d_fwheads;
        u_int                   d_maxsize;
+       off_t                   d_delmaxsize;
        u_int                   d_stripeoffset;
        u_int                   d_stripesize;
        char                    d_ident[DISK_IDENT_SIZE];
_______________________________________________
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