Author: mav
Date: Thu Nov  6 00:48:36 2014
New Revision: 274154
URL: https://svnweb.freebsd.org/changeset/base/274154

Log:
  Add to CTL support for logical block provisioning threshold notifications.
  
  For ZVOL-backed LUNs this allows to inform initiators if storage's used or
  available spaces get above/below the configured thresholds.
  
  MFC after:    2 weeks
  Sponsored by: iXsystems, Inc.

Modified:
  head/sys/cam/ctl/ctl.c
  head/sys/cam/ctl/ctl.h
  head/sys/cam/ctl/ctl_backend.h
  head/sys/cam/ctl/ctl_backend_block.c
  head/sys/cam/ctl/ctl_error.c
  head/sys/cam/ctl/ctl_private.h
  head/sys/cam/scsi/scsi_all.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
  head/sys/geom/geom_dev.c
  head/sys/sys/disk.h
  head/usr.sbin/ctladm/ctladm.8

Modified: head/sys/cam/ctl/ctl.c
==============================================================================
--- head/sys/cam/ctl/ctl.c      Wed Nov  5 23:59:52 2014        (r274153)
+++ head/sys/cam/ctl/ctl.c      Thu Nov  6 00:48:36 2014        (r274154)
@@ -137,7 +137,7 @@ static struct scsi_da_rw_recovery_page r
        /*correction_span*/0,
        /*head_offset_count*/0,
        /*data_strobe_offset_cnt*/0,
-       /*byte8*/0,
+       /*byte8*/SMS_RWER_LBPERE,
        /*write_retry_count*/0,
        /*reserved2*/0,
        /*recovery_time_limit*/{0, 0},
@@ -297,22 +297,58 @@ static struct scsi_info_exceptions_page 
        /*report_count*/{0, 0, 0, 0}
 };
 
-static struct scsi_logical_block_provisioning_page lbp_page_default = {
+#define CTL_LBPM_LEN   (sizeof(struct ctl_logical_block_provisioning_page) - 4)
+
+static struct ctl_logical_block_provisioning_page lbp_page_default = {{
        /*page_code*/SMS_INFO_EXCEPTIONS_PAGE | SMPH_SPF,
        /*subpage_code*/0x02,
-       /*page_length*/{0, sizeof(struct scsi_logical_block_provisioning_page) 
- 4},
+       /*page_length*/{CTL_LBPM_LEN >> 8, CTL_LBPM_LEN},
        /*flags*/0,
        /*reserved*/{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-       /*descr*/{}
+       /*descr*/{}},
+       {{/*flags*/0,
+         /*resource*/0x01,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}},
+        {/*flags*/0,
+         /*resource*/0x02,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}},
+        {/*flags*/0,
+         /*resource*/0xf1,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}},
+        {/*flags*/0,
+         /*resource*/0xf2,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}}
+       }
 };
 
-static struct scsi_logical_block_provisioning_page lbp_page_changeable = {
+static struct ctl_logical_block_provisioning_page lbp_page_changeable = {{
        /*page_code*/SMS_INFO_EXCEPTIONS_PAGE | SMPH_SPF,
        /*subpage_code*/0x02,
-       /*page_length*/{0, sizeof(struct scsi_logical_block_provisioning_page) 
- 4},
+       /*page_length*/{CTL_LBPM_LEN >> 8, CTL_LBPM_LEN},
        /*flags*/0,
        /*reserved*/{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-       /*descr*/{}
+       /*descr*/{}},
+       {{/*flags*/0,
+         /*resource*/0,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}},
+        {/*flags*/0,
+         /*resource*/0,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}},
+        {/*flags*/0,
+         /*resource*/0,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}},
+        {/*flags*/0,
+         /*resource*/0,
+         /*reserved*/{0, 0},
+         /*count*/{0, 0, 0, 0}}
+       }
 };
 
 /*
@@ -447,6 +483,7 @@ static void ctl_datamove_remote_read(uni
 static void ctl_datamove_remote(union ctl_io *io);
 static int ctl_process_done(union ctl_io *io);
 static void ctl_lun_thread(void *arg);
+static void ctl_thresh_thread(void *arg);
 static void ctl_work_thread(void *arg);
 static void ctl_enqueue_incoming(union ctl_io *io);
 static void ctl_enqueue_rtr(union ctl_io *io);
@@ -1085,6 +1122,15 @@ ctl_init(void)
                ctl_pool_free(other_pool);
                return (error);
        }
+       error = kproc_kthread_add(ctl_thresh_thread, softc,
+           &softc->ctl_proc, NULL, 0, 0, "ctl", "thresh");
+       if (error != 0) {
+               printf("error creating CTL threshold thread!\n");
+               ctl_pool_free(internal_pool);
+               ctl_pool_free(emergency_pool);
+               ctl_pool_free(other_pool);
+               return (error);
+       }
        if (bootverbose)
                printf("ctl: CAM Target Layer loaded\n");
 
@@ -3991,6 +4037,52 @@ ctl_copy_io(union ctl_io *src, union ctl
        dest->io_hdr.flags |= CTL_FLAG_INT_COPY;
 }
 
+static int
+ctl_expand_number(const char *buf, uint64_t *num)
+{
+       char *endptr;
+       uint64_t number;
+       unsigned shift;
+
+       number = strtoq(buf, &endptr, 0);
+
+       switch (tolower((unsigned char)*endptr)) {
+       case 'e':
+               shift = 60;
+               break;
+       case 'p':
+               shift = 50;
+               break;
+       case 't':
+               shift = 40;
+               break;
+       case 'g':
+               shift = 30;
+               break;
+       case 'm':
+               shift = 20;
+               break;
+       case 'k':
+               shift = 10;
+               break;
+       case 'b':
+       case '\0': /* No unit. */
+               *num = number;
+               return (0);
+       default:
+               /* Unrecognized unit. */
+               return (-1);
+       }
+
+       if ((number << shift) >> shift != number) {
+               /* Overflow */
+               return (-1);
+       }
+       *num = number << shift;
+       return (0);
+}
+
+
 /*
  * This routine could be used in the future to load default and/or saved
  * mode page parameters for a particuar lun.
@@ -4001,6 +4093,7 @@ ctl_init_page_index(struct ctl_lun *lun)
        int i;
        struct ctl_page_index *page_index;
        const char *value;
+       uint64_t ival;
 
        memcpy(&lun->mode_pages.index, page_index_template,
               sizeof(page_index_template));
@@ -4245,22 +4338,77 @@ ctl_init_page_index(struct ctl_lun *lun)
                                page_index->page_data =
                                        (uint8_t *)lun->mode_pages.ie_page;
                                break;
-                       case 0x02:
-                               
memcpy(&lun->mode_pages.lbp_page[CTL_PAGE_CURRENT],
+                       case 0x02: {
+                               struct ctl_logical_block_provisioning_page 
*page;
+
+                               
memcpy(&lun->mode_pages.lbp_page[CTL_PAGE_DEFAULT],
                                       &lbp_page_default,
                                       sizeof(lbp_page_default));
                                memcpy(&lun->mode_pages.lbp_page[
                                       CTL_PAGE_CHANGEABLE], 
&lbp_page_changeable,
                                       sizeof(lbp_page_changeable));
-                               
memcpy(&lun->mode_pages.lbp_page[CTL_PAGE_DEFAULT],
-                                      &lbp_page_default,
-                                      sizeof(lbp_page_default));
                                
memcpy(&lun->mode_pages.lbp_page[CTL_PAGE_SAVED],
                                       &lbp_page_default,
                                       sizeof(lbp_page_default));
+                               page = 
&lun->mode_pages.lbp_page[CTL_PAGE_SAVED];
+                               value = ctl_get_opt(&lun->be_lun->options,
+                                   "avail-threshold");
+                               if (value != NULL &&
+                                   ctl_expand_number(value, &ival) == 0) {
+                                       page->descr[0].flags |= SLBPPD_ENABLED |
+                                           SLBPPD_ARMING_DEC;
+                                       if (lun->be_lun->blocksize)
+                                               ival /= lun->be_lun->blocksize;
+                                       else
+                                               ival /= 512;
+                                       scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
+                                           page->descr[0].count);
+                               }
+                               value = ctl_get_opt(&lun->be_lun->options,
+                                   "used-threshold");
+                               if (value != NULL &&
+                                   ctl_expand_number(value, &ival) == 0) {
+                                       page->descr[1].flags |= SLBPPD_ENABLED |
+                                           SLBPPD_ARMING_INC;
+                                       if (lun->be_lun->blocksize)
+                                               ival /= lun->be_lun->blocksize;
+                                       else
+                                               ival /= 512;
+                                       scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
+                                           page->descr[1].count);
+                               }
+                               value = ctl_get_opt(&lun->be_lun->options,
+                                   "pool-avail-threshold");
+                               if (value != NULL &&
+                                   ctl_expand_number(value, &ival) == 0) {
+                                       page->descr[2].flags |= SLBPPD_ENABLED |
+                                           SLBPPD_ARMING_DEC;
+                                       if (lun->be_lun->blocksize)
+                                               ival /= lun->be_lun->blocksize;
+                                       else
+                                               ival /= 512;
+                                       scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
+                                           page->descr[2].count);
+                               }
+                               value = ctl_get_opt(&lun->be_lun->options,
+                                   "pool-used-threshold");
+                               if (value != NULL &&
+                                   ctl_expand_number(value, &ival) == 0) {
+                                       page->descr[3].flags |= SLBPPD_ENABLED |
+                                           SLBPPD_ARMING_INC;
+                                       if (lun->be_lun->blocksize)
+                                               ival /= lun->be_lun->blocksize;
+                                       else
+                                               ival /= 512;
+                                       scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
+                                           page->descr[3].count);
+                               }
+                               
memcpy(&lun->mode_pages.lbp_page[CTL_PAGE_CURRENT],
+                                      
&lun->mode_pages.lbp_page[CTL_PAGE_SAVED],
+                                      sizeof(lbp_page_default));
                                page_index->page_data =
                                        (uint8_t *)lun->mode_pages.lbp_page;
-                       }
+                       }}
                        break;
                }
                case SMS_VENDOR_SPECIFIC_PAGE:{
@@ -4319,13 +4467,13 @@ static int
 ctl_init_log_page_index(struct ctl_lun *lun)
 {
        struct ctl_page_index *page_index;
-       int i, j, prev;
+       int i, j, k, prev;
 
        memcpy(&lun->log_pages.index, log_page_index_template,
               sizeof(log_page_index_template));
 
        prev = -1;
-       for (i = 0, j = 0; i < CTL_NUM_LOG_PAGES; i++) {
+       for (i = 0, j = 0, k = 0; i < CTL_NUM_LOG_PAGES; i++) {
 
                page_index = &lun->log_pages.index[i];
                /*
@@ -4338,18 +4486,26 @@ ctl_init_log_page_index(struct ctl_lun *
                 && (page_index->page_flags & CTL_PAGE_FLAG_DISK_ONLY))
                        continue;
 
+               if (page_index->page_code == SLS_LOGICAL_BLOCK_PROVISIONING &&
+                   ((lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) == 0 ||
+                    lun->backend->lun_attr == NULL))
+                       continue;
+
                if (page_index->page_code != prev) {
                        lun->log_pages.pages_page[j] = page_index->page_code;
                        prev = page_index->page_code;
                        j++;
                }
-               lun->log_pages.subpages_page[i*2] = page_index->page_code;
-               lun->log_pages.subpages_page[i*2+1] = page_index->subpage;
+               lun->log_pages.subpages_page[k*2] = page_index->page_code;
+               lun->log_pages.subpages_page[k*2+1] = page_index->subpage;
+               k++;
        }
        lun->log_pages.index[0].page_data = &lun->log_pages.pages_page[0];
        lun->log_pages.index[0].page_len = j;
        lun->log_pages.index[1].page_data = &lun->log_pages.subpages_page[0];
-       lun->log_pages.index[1].page_len = i * 2;
+       lun->log_pages.index[1].page_len = k * 2;
+       lun->log_pages.index[2].page_data = &lun->log_pages.lbp_page[0];
+       lun->log_pages.index[2].page_len = 12*CTL_NUM_LBP_PARAMS;
 
        return (CTL_RETVAL_COMPLETE);
 }
@@ -6938,6 +7094,75 @@ ctl_mode_sense(struct ctl_scsiio *ctsio)
 }
 
 int
+ctl_lbp_log_sense_handler(struct ctl_scsiio *ctsio,
+                              struct ctl_page_index *page_index,
+                              int pc)
+{
+       struct ctl_lun *lun;
+       struct scsi_log_param_header *phdr;
+       uint8_t *data;
+       uint64_t val;
+
+       lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
+       data = page_index->page_data;
+
+       if (lun->backend->lun_attr != NULL &&
+           (val = lun->backend->lun_attr(lun->be_lun->be_lun, "blocksavail"))
+            != UINT64_MAX) {
+               phdr = (struct scsi_log_param_header *)data;
+               scsi_ulto2b(0x0001, phdr->param_code);
+               phdr->param_control = SLP_LBIN | SLP_LP;
+               phdr->param_len = 8;
+               data = (uint8_t *)(phdr + 1);
+               scsi_ulto4b(val >> CTL_LBP_EXPONENT, data);
+               data[4] = 0x01; /* per-LUN */
+               data += phdr->param_len;
+       }
+
+       if (lun->backend->lun_attr != NULL &&
+           (val = lun->backend->lun_attr(lun->be_lun->be_lun, "blocksused"))
+            != UINT64_MAX) {
+               phdr = (struct scsi_log_param_header *)data;
+               scsi_ulto2b(0x0002, phdr->param_code);
+               phdr->param_control = SLP_LBIN | SLP_LP;
+               phdr->param_len = 8;
+               data = (uint8_t *)(phdr + 1);
+               scsi_ulto4b(val >> CTL_LBP_EXPONENT, data);
+               data[4] = 0x02; /* per-pool */
+               data += phdr->param_len;
+       }
+
+       if (lun->backend->lun_attr != NULL &&
+           (val = lun->backend->lun_attr(lun->be_lun->be_lun, 
"poolblocksavail"))
+            != UINT64_MAX) {
+               phdr = (struct scsi_log_param_header *)data;
+               scsi_ulto2b(0x00f1, phdr->param_code);
+               phdr->param_control = SLP_LBIN | SLP_LP;
+               phdr->param_len = 8;
+               data = (uint8_t *)(phdr + 1);
+               scsi_ulto4b(val >> CTL_LBP_EXPONENT, data);
+               data[4] = 0x02; /* per-pool */
+               data += phdr->param_len;
+       }
+
+       if (lun->backend->lun_attr != NULL &&
+           (val = lun->backend->lun_attr(lun->be_lun->be_lun, 
"poolblocksused"))
+            != UINT64_MAX) {
+               phdr = (struct scsi_log_param_header *)data;
+               scsi_ulto2b(0x00f2, phdr->param_code);
+               phdr->param_control = SLP_LBIN | SLP_LP;
+               phdr->param_len = 8;
+               data = (uint8_t *)(phdr + 1);
+               scsi_ulto4b(val >> CTL_LBP_EXPONENT, data);
+               data[4] = 0x02; /* per-pool */
+               data += phdr->param_len;
+       }
+
+       page_index->page_len = data - page_index->page_data;
+       return (0);
+}
+
+int
 ctl_log_sense(struct ctl_scsiio *ctsio)
 {
        struct ctl_lun *lun;
@@ -10245,9 +10470,10 @@ ctl_inquiry_evpd_lbp(struct ctl_scsiio *
        lbp_ptr->page_code = SVPD_LBP;
        scsi_ulto2b(sizeof(*lbp_ptr) - 4, lbp_ptr->page_length);
        if (lun != NULL && lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) {
+               lbp_ptr->threshold_exponent = CTL_LBP_EXPONENT;
                lbp_ptr->flags = SVPD_LBP_UNMAP | SVPD_LBP_WS16 |
                    SVPD_LBP_WS10 | SVPD_LBP_RZ | SVPD_LBP_ANC_SUP;
-               lbp_ptr->prov_type = SVPD_LBP_RESOURCE;
+               lbp_ptr->prov_type = SVPD_LBP_THIN;
        }
 
        ctsio->scsi_status = SCSI_STATUS_OK;
@@ -13994,6 +14220,88 @@ ctl_lun_thread(void *arg)
 }
 
 static void
+ctl_thresh_thread(void *arg)
+{
+       struct ctl_softc *softc = (struct ctl_softc *)arg;
+       struct ctl_lun *lun;
+       struct ctl_be_lun *be_lun;
+       struct scsi_da_rw_recovery_page *rwpage;
+       struct ctl_logical_block_provisioning_page *page;
+       const char *attr;
+       uint64_t thres, val;
+       int i, e;
+
+       CTL_DEBUG_PRINT(("ctl_thresh_thread starting\n"));
+
+       for (;;) {
+               mtx_lock(&softc->ctl_lock);
+               STAILQ_FOREACH(lun, &softc->lun_list, links) {
+                       be_lun = lun->be_lun;
+                       if ((lun->flags & CTL_LUN_DISABLED) ||
+                           (lun->flags & CTL_LUN_OFFLINE) ||
+                           (be_lun->flags & CTL_LUN_FLAG_UNMAP) == 0 ||
+                           lun->backend->lun_attr == NULL)
+                               continue;
+                       rwpage = &lun->mode_pages.rw_er_page[CTL_PAGE_CURRENT];
+                       if ((rwpage->byte8 & SMS_RWER_LBPERE) == 0)
+                               continue;
+                       e = 0;
+                       page = &lun->mode_pages.lbp_page[CTL_PAGE_CURRENT];
+                       for (i = 0; i < CTL_NUM_LBP_THRESH; i++) {
+                               if ((page->descr[i].flags & SLBPPD_ENABLED) == 
0)
+                                       continue;
+                               thres = scsi_4btoul(page->descr[i].count);
+                               thres <<= CTL_LBP_EXPONENT;
+                               switch (page->descr[i].resource) {
+                               case 0x01:
+                                       attr = "blocksavail";
+                                       break;
+                               case 0x02:
+                                       attr = "blocksused";
+                                       break;
+                               case 0xf1:
+                                       attr = "poolblocksavail";
+                                       break;
+                               case 0xf2:
+                                       attr = "poolblocksused";
+                                       break;
+                               default:
+                                       continue;
+                               }
+                               mtx_unlock(&softc->ctl_lock); // XXX
+                               val = lun->backend->lun_attr(
+                                   lun->be_lun->be_lun, attr);
+                               mtx_lock(&softc->ctl_lock);
+                               if (val == UINT64_MAX)
+                                       continue;
+                               if ((page->descr[i].flags & SLBPPD_ARMING_MASK)
+                                   == SLBPPD_ARMING_INC)
+                                       e |= (val >= thres);
+                               else
+                                       e |= (val <= thres);
+                       }
+                       mtx_lock(&lun->lun_lock);
+                       if (e) {
+                               if (lun->lasttpt == 0 ||
+                                   time_uptime - lun->lasttpt >= 
CTL_LBP_UA_PERIOD) {
+                                       lun->lasttpt = time_uptime;
+                                       for (i = 0; i < CTL_MAX_INITIATORS; i++)
+                                               lun->pending_ua[i] |=
+                                                   CTL_UA_THIN_PROV_THRES;
+                               }
+                       } else {
+                               lun->lasttpt = 0;
+                               for (i = 0; i < CTL_MAX_INITIATORS; i++)
+                                       lun->pending_ua[i] &= 
~CTL_UA_THIN_PROV_THRES;
+                       }
+                       mtx_unlock(&lun->lun_lock);
+               }
+               mtx_unlock(&softc->ctl_lock);
+               pause("-", CTL_LBP_PERIOD * hz);
+       }
+}
+
+static void
 ctl_enqueue_incoming(union ctl_io *io)
 {
        struct ctl_softc *softc = control_softc;

Modified: head/sys/cam/ctl/ctl.h
==============================================================================
--- head/sys/cam/ctl/ctl.h      Wed Nov  5 23:59:52 2014        (r274153)
+++ head/sys/cam/ctl/ctl.h      Thu Nov  6 00:48:36 2014        (r274154)
@@ -127,7 +127,8 @@ typedef enum {
        CTL_UA_RES_RELEASE      = 0x0800,
        CTL_UA_REG_PREEMPT      = 0x1000,
        CTL_UA_ASYM_ACC_CHANGE  = 0x2000,
-       CTL_UA_CAPACITY_CHANGED = 0x4000
+       CTL_UA_CAPACITY_CHANGED = 0x4000,
+       CTL_UA_THIN_PROV_THRES  = 0x8000
 } ctl_ua_type;
 
 #ifdef _KERNEL
@@ -178,6 +179,9 @@ int ctl_debugconf_sp_sense_handler(struc
 int ctl_debugconf_sp_select_handler(struct ctl_scsiio *ctsio,
                                    struct ctl_page_index *page_index,
                                    uint8_t *page_ptr);
+int ctl_lbp_log_sense_handler(struct ctl_scsiio *ctsio,
+                                  struct ctl_page_index *page_index,
+                                  int pc);
 int ctl_config_move_done(union ctl_io *io);
 void ctl_datamove(union ctl_io *io);
 void ctl_done(union ctl_io *io);

Modified: head/sys/cam/ctl/ctl_backend.h
==============================================================================
--- head/sys/cam/ctl/ctl_backend.h      Wed Nov  5 23:59:52 2014        
(r274153)
+++ head/sys/cam/ctl/ctl_backend.h      Thu Nov  6 00:48:36 2014        
(r274154)
@@ -218,6 +218,7 @@ typedef void (*be_vfunc_t)(union ctl_io 
 typedef int (*be_ioctl_t)(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
                          struct thread *td);
 typedef int (*be_luninfo_t)(void *be_lun, struct sbuf *sb);
+typedef uint64_t (*be_lunattr_t)(void *be_lun, const char *attrname);
 
 struct ctl_backend_driver {
        char              name[CTL_BE_NAME_LEN]; /* passed to CTL */
@@ -229,6 +230,7 @@ struct ctl_backend_driver {
        be_func_t         config_write;          /* passed to CTL */
        be_ioctl_t        ioctl;                 /* passed to CTL */
        be_luninfo_t      lun_info;              /* passed to CTL */
+       be_lunattr_t      lun_attr;              /* passed to CTL */
 #ifdef CS_BE_CONFIG_MOVE_DONE_IS_NOT_USED
        be_func_t         config_move_done;      /* passed to backend */
 #endif

Modified: head/sys/cam/ctl/ctl_backend_block.c
==============================================================================
--- head/sys/cam/ctl/ctl_backend_block.c        Wed Nov  5 23:59:52 2014        
(r274153)
+++ head/sys/cam/ctl/ctl_backend_block.c        Thu Nov  6 00:48:36 2014        
(r274154)
@@ -145,6 +145,8 @@ struct ctl_be_block_lun;
 
 typedef void (*cbb_dispatch_t)(struct ctl_be_block_lun *be_lun,
                               struct ctl_be_block_io *beio);
+typedef uint64_t (*cbb_getattr_t)(struct ctl_be_block_lun *be_lun,
+                                 const char *attrname);
 
 /*
  * Backend LUN structure.  There is a 1:1 mapping between a block device
@@ -161,6 +163,7 @@ struct ctl_be_block_lun {
        cbb_dispatch_t dispatch;
        cbb_dispatch_t lun_flush;
        cbb_dispatch_t unmap;
+       cbb_getattr_t getattr;
        uma_zone_t lun_zone;
        uint64_t size_blocks;
        uint64_t size_bytes;
@@ -240,6 +243,8 @@ static void ctl_be_block_unmap_dev(struc
                                   struct ctl_be_block_io *beio);
 static void ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun,
                                      struct ctl_be_block_io *beio);
+static uint64_t ctl_be_block_getattr_dev(struct ctl_be_block_lun *be_lun,
+                                        const char *attrname);
 static void ctl_be_block_cw_dispatch(struct ctl_be_block_lun *be_lun,
                                    union ctl_io *io);
 static void ctl_be_block_dispatch(struct ctl_be_block_lun *be_lun,
@@ -272,6 +277,7 @@ static void ctl_be_block_lun_config_stat
 static int ctl_be_block_config_write(union ctl_io *io);
 static int ctl_be_block_config_read(union ctl_io *io);
 static int ctl_be_block_lun_info(void *be_lun, struct sbuf *sb);
+static uint64_t ctl_be_block_lun_attr(void *be_lun, const char *attrname);
 int ctl_be_block_init(void);
 
 static struct ctl_backend_driver ctl_be_block_driver = 
@@ -284,7 +290,8 @@ static struct ctl_backend_driver ctl_be_
        .config_read = ctl_be_block_config_read,
        .config_write = ctl_be_block_config_write,
        .ioctl = ctl_be_block_ioctl,
-       .lun_info = ctl_be_block_lun_info
+       .lun_info = ctl_be_block_lun_info,
+       .lun_attr = ctl_be_block_lun_attr
 };
 
 MALLOC_DEFINE(M_CTLBLK, "ctlblk", "Memory used for CTL block backend");
@@ -1012,6 +1019,24 @@ ctl_be_block_dispatch_dev(struct ctl_be_
        }
 }
 
+static uint64_t
+ctl_be_block_getattr_dev(struct ctl_be_block_lun *be_lun, const char *attrname)
+{
+       struct ctl_be_block_devdata     *dev_data = &be_lun->backend.dev;
+       struct diocgattr_arg    arg;
+       int                     error;
+
+       if (dev_data->csw == NULL || dev_data->csw->d_ioctl == NULL)
+               return (UINT64_MAX);
+       strlcpy(arg.name, attrname, sizeof(arg.name));
+       arg.len = sizeof(arg.value.off);
+       error = dev_data->csw->d_ioctl(dev_data->cdev,
+           DIOCGATTR, (caddr_t)&arg, FREAD, curthread);
+       if (error != 0)
+               return (UINT64_MAX);
+       return (arg.value.off);
+}
+
 static void
 ctl_be_block_cw_done_ws(struct ctl_be_block_io *beio)
 {
@@ -1647,6 +1672,7 @@ ctl_be_block_open_dev(struct ctl_be_bloc
                be_lun->dispatch = ctl_be_block_dispatch_dev;
        be_lun->lun_flush = ctl_be_block_flush_dev;
        be_lun->unmap = ctl_be_block_unmap_dev;
+       be_lun->getattr = ctl_be_block_getattr_dev;
 
        error = VOP_GETATTR(be_lun->vn, &vattr, NOCRED);
        if (error) {
@@ -1993,10 +2019,10 @@ ctl_be_block_create(struct ctl_be_block_
                }
                num_threads = tmp_num_threads;
        }
-       unmap = 0;
+       unmap = (be_lun->dispatch == ctl_be_block_dispatch_zvol);
        value = ctl_get_opt(&be_lun->ctl_be_lun.options, "unmap");
-       if (value != NULL && strcmp(value, "on") == 0)
-               unmap = 1;
+       if (value != NULL)
+               unmap = (strcmp(value, "on") == 0);
 
        be_lun->flags = CTL_BE_BLOCK_LUN_UNCONFIGURED;
        be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY;
@@ -2582,6 +2608,16 @@ bailout:
        return (retval);
 }
 
+static uint64_t
+ctl_be_block_lun_attr(void *be_lun, const char *attrname)
+{
+       struct ctl_be_block_lun *lun = (struct ctl_be_block_lun *)be_lun;
+
+       if (lun->getattr == NULL)
+               return (UINT64_MAX);
+       return (lun->getattr(lun, attrname));
+}
+
 int
 ctl_be_block_init(void)
 {

Modified: head/sys/cam/ctl/ctl_error.c
==============================================================================
--- head/sys/cam/ctl/ctl_error.c        Wed Nov  5 23:59:52 2014        
(r274153)
+++ head/sys/cam/ctl/ctl_error.c        Thu Nov  6 00:48:36 2014        
(r274154)
@@ -463,6 +463,11 @@ ctl_build_ua(ctl_ua_type *ua_type, struc
                asc = 0x2A;
                ascq = 0x09;
                break;
+       case CTL_UA_THIN_PROV_THRES:
+               /* 38h/07n  THIN PROVISIONING SOFT THRESHOLD REACHED */
+               asc = 0x38;
+               ascq = 0x07;
+               break;
        default:
                panic("ctl_build_ua: Unknown UA %x", ua_to_build);
        }

Modified: head/sys/cam/ctl/ctl_private.h
==============================================================================
--- head/sys/cam/ctl/ctl_private.h      Wed Nov  5 23:59:52 2014        
(r274153)
+++ head/sys/cam/ctl/ctl_private.h      Thu Nov  6 00:48:36 2014        
(r274154)
@@ -302,6 +302,17 @@ struct ctl_page_index {
 #define        CTL_PAGE_DEFAULT        0x02
 #define        CTL_PAGE_SAVED          0x03
 
+#define CTL_NUM_LBP_PARAMS     4
+#define CTL_NUM_LBP_THRESH     4
+#define CTL_LBP_EXPONENT       11      /* 2048 sectors */
+#define CTL_LBP_PERIOD         10      /* 10 seconds */
+#define CTL_LBP_UA_PERIOD      300     /* 5 minutes */
+
+struct ctl_logical_block_provisioning_page {
+       struct scsi_logical_block_provisioning_page     main;
+       struct scsi_logical_block_provisioning_page_descr 
descr[CTL_NUM_LBP_THRESH];
+};
+
 static const struct ctl_page_index page_index_template[] = {
        {SMS_RW_ERROR_RECOVERY_PAGE, 0, sizeof(struct 
scsi_da_rw_recovery_page), NULL,
         CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
@@ -316,7 +327,7 @@ static const struct ctl_page_index page_
        {SMS_INFO_EXCEPTIONS_PAGE, 0, sizeof(struct scsi_info_exceptions_page), 
NULL,
         CTL_PAGE_FLAG_NONE, NULL, NULL},
        {SMS_INFO_EXCEPTIONS_PAGE | SMPH_SPF, 0x02,
-        sizeof(struct scsi_logical_block_provisioning_page), NULL,
+        sizeof(struct ctl_logical_block_provisioning_page), NULL,
         CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
        {SMS_VENDOR_SPECIFIC_PAGE | SMPH_SPF, DBGCNF_SUBPAGE_CODE,
         sizeof(struct copan_debugconf_subpage), NULL, CTL_PAGE_FLAG_NONE,
@@ -333,7 +344,7 @@ struct ctl_mode_pages {
        struct scsi_caching_page        caching_page[4];
        struct scsi_control_page        control_page[4];
        struct scsi_info_exceptions_page ie_page[4];
-       struct scsi_logical_block_provisioning_page lbp_page[4];
+       struct ctl_logical_block_provisioning_page lbp_page[4];
        struct copan_debugconf_subpage  debugconf_subpage[4];
        struct ctl_page_index           index[CTL_NUM_MODE_PAGES];
 };
@@ -343,6 +354,8 @@ static const struct ctl_page_index log_p
         CTL_PAGE_FLAG_NONE, NULL, NULL},
        {SLS_SUPPORTED_PAGES_PAGE, SLS_SUPPORTED_SUBPAGES_SUBPAGE, 0, NULL,
         CTL_PAGE_FLAG_NONE, NULL, NULL},
+       {SLS_LOGICAL_BLOCK_PROVISIONING, 0, 0, NULL,
+        CTL_PAGE_FLAG_NONE, ctl_lbp_log_sense_handler, NULL},
 };
 
 #define        CTL_NUM_LOG_PAGES sizeof(log_page_index_template)/   \
@@ -351,6 +364,7 @@ static const struct ctl_page_index log_p
 struct ctl_log_pages {
        uint8_t                         pages_page[CTL_NUM_LOG_PAGES];
        uint8_t                         subpages_page[CTL_NUM_LOG_PAGES * 2];
+       uint8_t                         lbp_page[12*CTL_NUM_LBP_PARAMS];
        struct ctl_page_index           index[CTL_NUM_LOG_PAGES];
 };
 
@@ -411,6 +425,7 @@ struct ctl_lun {
        struct scsi_sense_data          pending_sense[CTL_MAX_INITIATORS];
 #endif
        ctl_ua_type                     pending_ua[CTL_MAX_INITIATORS];
+       time_t                          lasttpt;
        struct ctl_mode_pages           mode_pages;
        struct ctl_log_pages            log_pages;
        struct ctl_lun_io_stats         stats;

Modified: head/sys/cam/scsi/scsi_all.h
==============================================================================
--- head/sys/cam/scsi/scsi_all.h        Wed Nov  5 23:59:52 2014        
(r274153)
+++ head/sys/cam/scsi/scsi_all.h        Thu Nov  6 00:48:36 2014        
(r274154)
@@ -559,6 +559,7 @@ struct scsi_log_sense
 #define        SLS_ERROR_VERIFY_PAGE           0x05
 #define        SLS_ERROR_NONMEDIUM_PAGE        0x06
 #define        SLS_ERROR_LASTN_PAGE            0x07
+#define        SLS_LOGICAL_BLOCK_PROVISIONING  0x0c
 #define        SLS_SELF_TEST_PAGE              0x10
 #define        SLS_IE_PAGE                     0x2f
 #define        SLS_PAGE_CTRL_MASK              0xC0
@@ -740,6 +741,11 @@ struct scsi_info_exceptions_page {
 
 struct scsi_logical_block_provisioning_page_descr {
        uint8_t flags;
+#define        SLBPPD_ENABLED          0x80
+#define        SLBPPD_TYPE_MASK        0x38
+#define        SLBPPD_ARMING_MASK      0x07
+#define        SLBPPD_ARMING_DEC       0x02
+#define        SLBPPD_ARMING_INC       0x01
        uint8_t resource;
        uint8_t reserved[2];
        uint8_t count[4];

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c  Wed Nov  5 
23:59:52 2014        (r274153)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c  Thu Nov  6 
00:48:36 2014        (r274154)
@@ -2459,10 +2459,38 @@ zvol_geom_start(struct bio *bp)
                        goto enqueue;
                zvol_strategy(bp);
                break;
-       case BIO_GETATTR:
+       case BIO_GETATTR: {
+               spa_t *spa = dmu_objset_spa(zv->zv_objset);
+               uint64_t refd, avail, usedobjs, availobjs, val;
+
                if (g_handleattr_int(bp, "GEOM::candelete", 1))
                        return;
+               if (strcmp(bp->bio_attribute, "blocksavail") == 0) {
+                       dmu_objset_space(zv->zv_objset, &refd, &avail,
+                           &usedobjs, &availobjs);
+                       if (g_handleattr_off_t(bp, "blocksavail",
+                           avail / DEV_BSIZE))
+                               return;
+               } else if (strcmp(bp->bio_attribute, "blocksused") == 0) {
+                       dmu_objset_space(zv->zv_objset, &refd, &avail,
+                           &usedobjs, &availobjs);
+                       if (g_handleattr_off_t(bp, "blocksused",
+                           refd / DEV_BSIZE))
+                               return;
+               } else if (strcmp(bp->bio_attribute, "poolblocksavail") == 0) {
+                       avail = metaslab_class_get_space(spa_normal_class(spa));
+                       avail -= 
metaslab_class_get_alloc(spa_normal_class(spa));
+                       if (g_handleattr_off_t(bp, "poolblocksavail",
+                           avail / DEV_BSIZE))
+                               return;
+               } else if (strcmp(bp->bio_attribute, "poolblocksused") == 0) {
+                       refd = metaslab_class_get_alloc(spa_normal_class(spa));
+                       if (g_handleattr_off_t(bp, "poolblocksused",
+                           refd / DEV_BSIZE))
+                               return;
+               }
                /* FALLTHROUGH */
+       }
        default:
                g_io_deliver(bp, EOPNOTSUPP);
                break;
@@ -2861,6 +2889,30 @@ zvol_d_ioctl(struct cdev *dev, u_long cm
        case DIOCGSTRIPEOFFSET:
                *(off_t *)data = 0;
                break;
+       case DIOCGATTR: {
+               spa_t *spa = dmu_objset_spa(zv->zv_objset);
+               struct diocgattr_arg *arg = (struct diocgattr_arg *)data;
+               uint64_t refd, avail, usedobjs, availobjs;
+
+               if (strcmp(arg->name, "blocksavail") == 0) {
+                       dmu_objset_space(zv->zv_objset, &refd, &avail,
+                           &usedobjs, &availobjs);
+                       arg->value.off = avail / DEV_BSIZE;
+               } else if (strcmp(arg->name, "blocksused") == 0) {
+                       dmu_objset_space(zv->zv_objset, &refd, &avail,
+                           &usedobjs, &availobjs);
+                       arg->value.off = refd / DEV_BSIZE;
+               } else if (strcmp(arg->name, "poolblocksavail") == 0) {
+                       avail = metaslab_class_get_space(spa_normal_class(spa));
+                       avail -= 
metaslab_class_get_alloc(spa_normal_class(spa));
+                       arg->value.off = avail / DEV_BSIZE;
+               } else if (strcmp(arg->name, "poolblocksused") == 0) {
+                       refd = metaslab_class_get_alloc(spa_normal_class(spa));
+                       arg->value.off = refd / DEV_BSIZE;
+               } else
+                       error = ENOIOCTL;
+               break;
+       }
        default:
                error = ENOIOCTL;
        }

Modified: head/sys/geom/geom_dev.c
==============================================================================
--- head/sys/geom/geom_dev.c    Wed Nov  5 23:59:52 2014        (r274153)
+++ head/sys/geom/geom_dev.c    Thu Nov  6 00:48:36 2014        (r274154)
@@ -510,6 +510,16 @@ g_dev_ioctl(struct cdev *dev, u_long cmd
                if (error == 0 && *(char *)data == '\0')
                        error = ENOENT;
                break;
+       case DIOCGATTR: {
+               struct diocgattr_arg *arg = (struct diocgattr_arg *)data;
+
+               if (arg->len > sizeof(arg->value)) {
+                       error = EINVAL;
+                       break;
+               }
+               error = g_io_getattr(arg->name, cp, &arg->len, &arg->value);
+               break;
+       }
        default:
                if (cp->provider->geom->ioctl != NULL) {
                        error = cp->provider->geom->ioctl(cp->provider, cmd, 
data, fflag, td);

Modified: head/sys/sys/disk.h
==============================================================================
--- head/sys/sys/disk.h Wed Nov  5 23:59:52 2014        (r274153)
+++ head/sys/sys/disk.h Thu Nov  6 00:48:36 2014        (r274154)
@@ -124,4 +124,14 @@ void disk_err(struct bio *bp, const char
         * occupant of that location.
         */
 
+struct diocgattr_arg {
+       char name[64];
+       int len;
+       union {
+               char str[DISK_IDENT_SIZE];
+               off_t off;
+       } value;
+};
+#define        DIOCGATTR _IOWR('d', 142, struct diocgattr_arg)
+
 #endif /* _SYS_DISK_H_ */

Modified: head/usr.sbin/ctladm/ctladm.8
==============================================================================
--- head/usr.sbin/ctladm/ctladm.8       Wed Nov  5 23:59:52 2014        
(r274153)
+++ head/usr.sbin/ctladm/ctladm.8       Thu Nov  6 00:48:36 2014        
(r274154)
@@ -34,7 +34,7 @@
 .\" $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.8#3 $
 .\" $FreeBSD$
 .\"
-.Dd October 26, 2014
+.Dd November 5, 2014
 .Dt CTLADM 8
 .Os
 .Sh NAME
@@ -1003,6 +1003,14 @@ Specifies nominal form factor of the dev
 2 -- 3.5", 3 -- 2.5", 4 -- 1.8", 5 -- less then 1.8".
 .It Va unmap
 Set to "on", enables UNMAP support for the LUN, if supported by the backend.
+.It Va avail-threshold
+.It Va used-threshold
+.It Va pool-avail-threshold
+.It Va pool-used-threshold
+Set per-LUN/-pool thin provisioning soft thresholds for ZVOL-backed LUNs.
+LUN will establish UNIT ATTENTION condition if its or pool available space
+get below configured avail values, or its or pool used space get above
+configured used values.
 .It Va writecache
 Set to "off", disables write caching for the LUN, if supported by the backend.
 .El
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to