Print this page
701 UNMAP support for COMSTAR
Contributed by: Sumit Gupta <sumit.gupta@nexenta.com>
Reviewed by: Garrett D'Amore <garrett@nexenta.com>
Rich Lowe's comments
*** 18,27 ****
--- 18,29 ----
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
*** 81,91 ****
uint32_t *err_ret);
int sbd_get_global_props(sbd_global_props_t *oslp, uint32_t oslp_sz,
uint32_t *err_ret);
int sbd_get_lu_props(sbd_lu_props_t *islp, uint32_t islp_sz,
sbd_lu_props_t *oslp, uint32_t oslp_sz, uint32_t *err_ret);
! char *sbd_get_zvol_name(sbd_lu_t *sl);
sbd_status_t sbd_create_zfs_meta_object(sbd_lu_t *sl);
sbd_status_t sbd_open_zfs_meta(sbd_lu_t *sl);
sbd_status_t sbd_read_zfs_meta(sbd_lu_t *sl, uint8_t *buf, uint64_t sz,
uint64_t off);
sbd_status_t sbd_write_zfs_meta(sbd_lu_t *sl, uint8_t *buf, uint64_t sz,
--- 83,94 ----
uint32_t *err_ret);
int sbd_get_global_props(sbd_global_props_t *oslp, uint32_t oslp_sz,
uint32_t *err_ret);
int sbd_get_lu_props(sbd_lu_props_t *islp, uint32_t islp_sz,
sbd_lu_props_t *oslp, uint32_t oslp_sz, uint32_t *err_ret);
! static int sbd_get_unmap_props(sbd_unmap_props_t *sup, sbd_unmap_props_t *osup,
! uint32_t *err_ret);
sbd_status_t sbd_create_zfs_meta_object(sbd_lu_t *sl);
sbd_status_t sbd_open_zfs_meta(sbd_lu_t *sl);
sbd_status_t sbd_read_zfs_meta(sbd_lu_t *sl, uint8_t *buf, uint64_t sz,
uint64_t off);
sbd_status_t sbd_write_zfs_meta(sbd_lu_t *sl, uint8_t *buf, uint64_t sz,
*** 94,103 ****
--- 97,108 ----
int sbd_is_zvol(char *path);
int sbd_zvolget(char *zvol_name, char **comstarprop);
int sbd_zvolset(char *zvol_name, char *comstarprop);
char sbd_ctoi(char c);
void sbd_close_lu(sbd_lu_t *sl);
+ static nvlist_t *sbd_zvol_get_props(char *zvol_name);
+ static int sbd_zvol_set_props(char *zvol_name, nvlist_t *nv);
static ldi_ident_t sbd_zfs_ident;
static stmf_lu_provider_t *sbd_lp;
static sbd_lu_t *sbd_lu_list = NULL;
static kmutex_t sbd_lock;
*** 446,455 ****
--- 451,472 ----
}
mutex_exit(&sbd_lock);
ret = 0;
iocd->stmf_error = 0;
break;
+ case SBD_IOCTL_GET_UNMAP_PROPS:
+ if (iocd->stmf_ibuf_size < sizeof (sbd_unmap_props_t)) {
+ ret = EFAULT;
+ break;
+ }
+ if (iocd->stmf_obuf_size < sizeof (sbd_unmap_props_t)) {
+ ret = EINVAL;
+ break;
+ }
+ ret = sbd_get_unmap_props((sbd_unmap_props_t *)ibuf,
+ (sbd_unmap_props_t *)obuf, &iocd->stmf_error);
+ break;
default:
ret = ENOTTY;
}
if (ret == 0) {
*** 1370,1385 ****
--- 1387,1467 ----
ret = sbd_write_meta_section(sl, (sm_section_hdr_t *)sli);
kmem_free(sli, sizeof (*sli) + s);
return (ret);
}
+ /*
+ * Will scribble SL_UNMAP_ENABLED into sl_flags if we succeed.
+ */
+ static void
+ do_unmap_setup(sbd_lu_t *sl)
+ {
+ char *file = NULL;
+ nvlist_t *nv = NULL, *nv2 = NULL;
+ boolean_t already_compressing = B_FALSE;
+ uint64_t val;
+
+ ASSERT((sl->sl_flags & SL_UNMAP_ENABLED) == 0);
+
+ if ((sl->sl_flags & SL_ZFS_META) == 0)
+ return; /* No UNMAP for you. */
+
+ file = sbd_get_zvol_name(sl);
+ if (file == NULL) {
+ cmn_err(CE_WARN, "sbd has zfs meta but no zvol name");
+ return; /* No UNMAP for you. */
+ }
+ nv = sbd_zvol_get_props(file);
+ if (nv == NULL) {
+ cmn_err(CE_WARN, "unable to get zvol props");
+ kmem_free(file, strlen(file) + 1);
+ return; /* No UNMAP for you. */
+ }
+
+ /* See if compression is supported, but turned off on this dataset. */
+ if (nvlist_lookup_nvlist(nv, "compression", &nv2) == 0 &&
+ nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val) == 0) {
+ already_compressing = (val != ZIO_COMPRESS_OFF);
+ } else {
+ cmn_err(CE_WARN, "prop lookup for compression failed");
+ }
+
+ /*
+ * Check refreservation because no refreservation means this is a
+ * sparse zvol and we can enable SCSI unmap.
+ */
+ if (nvlist_lookup_nvlist(nv, "refreservation", &nv2) != 0) {
+ cmn_err(CE_WARN, "lookup for refreservation nvlist failed");
+ } else if (nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val) != 0) {
+ cmn_err(CE_WARN, "prop lookup for refreservation failed");
+ } else if (val == 0) {
+ sl->sl_flags |= SL_UNMAP_ENABLED;
+ if (!already_compressing) {
+ nvlist_free(nv);
+ nv = NULL;
+ if (nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) != 0 ||
+ nvlist_add_uint64(nv, "compression",
+ ZIO_COMPRESS_ZLE) != 0 ||
+ sbd_zvol_set_props(file, nv)) {
+ cmn_err(CE_WARN, "Setting zle compression "
+ "failed for zvol %s", file);
+ }
+ }
+ }
+
+ nvlist_free(nv);
+ kmem_free(file, strlen(file) + 1);
+ }
+
int
sbd_populate_and_register_lu(sbd_lu_t *sl, uint32_t *err_ret)
{
stmf_lu_t *lu = sl->sl_lu;
stmf_status_t ret;
+ do_unmap_setup(sl);
+
lu->lu_id = (scsi_devid_desc_t *)sl->sl_device_id;
if (sl->sl_alias) {
lu->lu_alias = sl->sl_alias;
} else {
lu->lu_alias = sl->sl_name;
*** 3130,3139 ****
--- 3212,3261 ----
rw_exit(&sbd_global_prop_lock);
return (0);
}
+ static int
+ sbd_get_unmap_props(sbd_unmap_props_t *sup,
+ sbd_unmap_props_t *osup, uint32_t *err_ret)
+ {
+ sbd_status_t sret;
+ sbd_lu_t *sl = NULL;
+
+ if (sup->sup_guid_valid) {
+ sret = sbd_find_and_lock_lu(sup->sup_guid,
+ NULL, SL_OP_LU_PROPS, &sl);
+ } else {
+ sret = sbd_find_and_lock_lu(NULL,
+ (uint8_t *)sup->sup_zvol_path, SL_OP_LU_PROPS,
+ &sl);
+ }
+ if (sret != SBD_SUCCESS) {
+ if (sret == SBD_BUSY) {
+ *err_ret = SBD_RET_LU_BUSY;
+ return (EBUSY);
+ } else if (sret == SBD_NOT_FOUND) {
+ *err_ret = SBD_RET_NOT_FOUND;
+ return (ENOENT);
+ }
+ return (EIO);
+ }
+
+ sup->sup_found_lu = 1;
+ sup->sup_guid_valid = 1;
+ bcopy(sl->sl_device_id + 4, sup->sup_guid, 16);
+ if (sl->sl_flags & SL_UNMAP_ENABLED)
+ sup->sup_unmap_enabled = 1;
+ else
+ sup->sup_unmap_enabled = 0;
+
+ *osup = *sup;
+ sl->sl_trans_op = SL_OP_NONE;
+
+ return (0);
+ }
+
int
sbd_get_lu_props(sbd_lu_props_t *islp, uint32_t islp_sz,
sbd_lu_props_t *oslp, uint32_t oslp_sz, uint32_t *err_ret)
{
sbd_status_t sret;
*** 3620,3625 ****
--- 3742,3831 ----
kmem_free(packed, len);
out:
nvlist_free(nv);
(void) ldi_close(zfs_lh, FREAD|FWRITE, kcred);
return (rc);
+ }
+
+ static nvlist_t *
+ sbd_zvol_get_props(char *zvol_name)
+ {
+ ldi_handle_t zfs_lh;
+ nvlist_t *nv = NULL;
+ zfs_cmd_t *zc;
+ int size = 4096;
+ int unused;
+ int rc;
+
+ if ((rc = ldi_open_by_name("/dev/zfs", FREAD | FWRITE, kcred,
+ &zfs_lh, sbd_zfs_ident)) != 0) {
+ cmn_err(CE_WARN, "ldi_open %d", rc);
+ return (NULL);
+ }
+
+ zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
+ (void) strlcpy(zc->zc_name, zvol_name, sizeof (zc->zc_name));
+ szg_again:
+ zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(size,
+ KM_SLEEP);
+ zc->zc_nvlist_dst_size = size;
+ rc = ldi_ioctl(zfs_lh, ZFS_IOC_OBJSET_STATS, (intptr_t)zc,
+ FKIOCTL, kcred, &unused);
+ /*
+ * ENOMEM means the list is larger than what we've allocated
+ * ldi_ioctl will fail with ENOMEM only once
+ */
+ if (rc == ENOMEM) {
+ int newsize;
+ newsize = zc->zc_nvlist_dst_size;
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+ size = newsize;
+ goto szg_again;
+ } else if (rc != 0) {
+ goto szg_out;
+ }
+ rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, &nv, 0);
+ ASSERT(rc == 0); /* nvlist_unpack should not fail */
+ szg_out:
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+ kmem_free(zc, sizeof (zfs_cmd_t));
+ (void) ldi_close(zfs_lh, FREAD|FWRITE, kcred);
+
+ return (nv);
+ }
+
+ static int
+ sbd_zvol_set_props(char *zvol_name, nvlist_t *nv)
+ {
+ ldi_handle_t zfs_lh;
+ char *packed = NULL;
+ size_t len;
+ zfs_cmd_t *zc;
+ int unused;
+ int rc;
+
+ if ((rc = ldi_open_by_name("/dev/zfs", FREAD | FWRITE, kcred,
+ &zfs_lh, sbd_zfs_ident)) != 0) {
+ cmn_err(CE_WARN, "ldi_open %d", rc);
+ return (ENXIO);
+ }
+ if ((rc = nvlist_pack(nv, &packed, &len, NV_ENCODE_NATIVE, KM_SLEEP))) {
+ goto szs_out;
+ }
+
+ zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
+ (void) strlcpy(zc->zc_name, zvol_name, sizeof (zc->zc_name));
+ zc->zc_nvlist_src = (uint64_t)(intptr_t)packed;
+ zc->zc_nvlist_src_size = len;
+ rc = ldi_ioctl(zfs_lh, ZFS_IOC_SET_PROP, (intptr_t)zc,
+ FKIOCTL, kcred, &unused);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "ioctl failed %d", rc);
+ }
+ kmem_free(zc, sizeof (zfs_cmd_t));
+ if (packed)
+ kmem_free(packed, len);
+ szs_out:
+ (void) ldi_close(zfs_lh, FREAD|FWRITE, kcred);
+ return (rc);
}