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); }