From 08a490350913f23895e719a6f17416fa3810566c Mon Sep 17 00:00:00 2001 From: Faruk Kasumovic Date: Mon, 8 Jan 2018 15:29:42 +0100 Subject: [PATCH] - Ability to create pool with spares, and l2cache This breaks previous API PoolCreate function call, changes to fix this break will be necessary. --- zpool.c | 24 +++++ zpool.go | 268 ++++++++++++++++++++++++++++++++++---------------- zpool.h | 2 + zpool_test.go | 27 ++++- 4 files changed, 230 insertions(+), 91 deletions(-) diff --git a/zpool.c b/zpool.c index c63665a..6cd0d49 100644 --- a/zpool.c +++ b/zpool.c @@ -408,6 +408,30 @@ vdev_children_ptr get_vdev_children(nvlist_t *nv) { return children; } +vdev_children_ptr get_vdev_spares(nvlist_t *nv) { + int r; + vdev_children_ptr children = malloc(sizeof(vdev_children_t)); + memset(children, 0, sizeof(vdev_children_t)); + r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &(children->first), &(children->count)); + if (r != 0) { + free(children); + return NULL; + } + return children; +} + +vdev_children_ptr get_vdev_l2cache(nvlist_t *nv) { + int r; + vdev_children_ptr children = malloc(sizeof(vdev_children_t)); + memset(children, 0, sizeof(vdev_children_t)); + r = nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &(children->first), &(children->count)); + if (r != 0) { + free(children); + return NULL; + } + return children; +} + const char *get_vdev_path(nvlist_ptr nv) { char *path = NULL; uint64_t notpresent = 0; diff --git a/zpool.go b/zpool.go index 9739352..9a6fba1 100644 --- a/zpool.go +++ b/zpool.go @@ -101,6 +101,9 @@ type PoolScanStat struct { type VDevTree struct { Type VDevType Devices []VDevTree // groups other devices (e.g. mirror) + Spares []VDevTree + L2Cache []VDevTree + Logs []VDevTree Parity uint Path string Name string @@ -233,6 +236,60 @@ func poolGetConfig(name string, nv C.nvlist_ptr) (vdevs VDevTree, err error) { } vdevs.Devices = append(vdevs.Devices, vdev) } + if vdevs.Spares, err = poolGetSpares(name, nv); err != nil { + return + } + if vdevs.L2Cache, err = poolGetL2Cache(name, nv); err != nil { + return + } + return +} + +func poolGetSpares(name string, nv C.nvlist_ptr) (vdevs []VDevTree, err error) { + // Fetch the spares + var spares C.vdev_children_ptr + spares = C.get_vdev_spares(nv) + if spares != nil { + // this object that reference spares and count should be deallocated from memory + defer C.free(unsafe.Pointer(spares)) + vdevs = make([]VDevTree, 0, spares.count) + } + for c := C.uint_t(0); spares != nil && c < spares.count; c++ { + vname := C.zpool_vdev_name(C.libzfsHandle, nil, C.nvlist_array_at(spares.first, c), + C.B_TRUE) + var vdev VDevTree + vdev, err = poolGetConfig(C.GoString(vname), + C.nvlist_array_at(spares.first, c)) + C.free(unsafe.Pointer(vname)) + if err != nil { + return + } + vdevs = append(vdevs, vdev) + } + return +} + +func poolGetL2Cache(name string, nv C.nvlist_ptr) (vdevs []VDevTree, err error) { + // Fetch the spares + var l2cache C.vdev_children_ptr + l2cache = C.get_vdev_l2cache(nv) + if l2cache != nil { + // this object that reference l2cache and count should be deallocated from memory + defer C.free(unsafe.Pointer(l2cache)) + vdevs = make([]VDevTree, 0, l2cache.count) + } + for c := C.uint_t(0); l2cache != nil && c < l2cache.count; c++ { + vname := C.zpool_vdev_name(C.libzfsHandle, nil, C.nvlist_array_at(l2cache.first, c), + C.B_TRUE) + var vdev VDevTree + vdev, err = poolGetConfig(C.GoString(vname), + C.nvlist_array_at(l2cache.first, c)) + C.free(unsafe.Pointer(vname)) + if err != nil { + return + } + vdevs = append(vdevs, vdev) + } return } @@ -651,7 +708,52 @@ func toCDatasetProperties(props DatasetProperties) (cprops C.nvlist_ptr) { return } -func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, +func buildVdev(vdev VDevTree, ashift int) (nvvdev *C.struct_nvlist, err error) { + if r := C.nvlist_alloc(&nvvdev, C.NV_UNIQUE_NAME, 0); r != 0 { + err = errors.New("Failed to allocate vdev") + return + } + csType := C.CString(string(vdev.Type)) + r := C.nvlist_add_string(nvvdev, C.sZPOOL_CONFIG_TYPE, + csType) + C.free(unsafe.Pointer(csType)) + if r != 0 { + err = errors.New("Failed to set vdev type") + return + } + if r := C.nvlist_add_uint64(nvvdev, C.sZPOOL_CONFIG_IS_LOG, + vdev.isLog()); r != 0 { + err = errors.New("Failed to allocate vdev (is_log)") + return + } + if r := C.nvlist_add_uint64(nvvdev, + C.sZPOOL_CONFIG_WHOLE_DISK, 1); r != 0 { + err = errors.New("Failed to allocate vdev nvvdev (whdisk)") + return + } + if len(vdev.Path) > 0 { + csPath := C.CString(vdev.Path) + r := C.nvlist_add_string( + nvvdev, C.sZPOOL_CONFIG_PATH, + csPath) + C.free(unsafe.Pointer(csPath)) + if r != 0 { + err = errors.New("Failed to allocate vdev nvvdev (type)") + return + } + if ashift > 0 { + if r := C.nvlist_add_uint64(nvvdev, + C.sZPOOL_CONFIG_ASHIFT, + C.uint64_t(ashift)); r != 0 { + err = errors.New("Failed to allocate vdev nvvdev (ashift)") + return + } + } + } + return +} + +func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs, spares, l2cache []VDevTree, props PoolProperties) (err error) { count := len(vdevs) if count == 0 { @@ -663,28 +765,9 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, return } defer C.nvlist_free_array(childrens) - spares := C.nvlist_alloc_array(C.int(count)) - if childrens == nil { - err = errors.New("No enough memory") - return - } - nspares := 0 - defer C.nvlist_free_array(spares) - l2cache := C.nvlist_alloc_array(C.int(count)) - if childrens == nil { - err = errors.New("No enough memory") - return - } - nl2cache := 0 - defer C.nvlist_free_array(l2cache) for i, vdev := range vdevs { grouping, mindevs, maxdevs := vdev.isGrouping() var child *C.struct_nvlist - // fmt.Println(vdev.Type) - if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 { - err = errors.New("Failed to allocate vdev") - return - } vcount := len(vdev.Devices) if vcount < mindevs || vcount > maxdevs { err = fmt.Errorf( @@ -692,20 +775,19 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, vdev.Type, mindevs, maxdevs) return } - csType := C.CString(string(vdev.Type)) - r := C.nvlist_add_string(child, C.sZPOOL_CONFIG_TYPE, - csType) - C.free(unsafe.Pointer(csType)) - if r != 0 { - err = errors.New("Failed to set vdev type") - return - } - if r := C.nvlist_add_uint64(child, C.sZPOOL_CONFIG_IS_LOG, - vdev.isLog()); r != 0 { - err = errors.New("Failed to allocate vdev (is_log)") - return - } if grouping { + if r := C.nvlist_alloc(&child, C.NV_UNIQUE_NAME, 0); r != 0 { + err = errors.New("Failed to allocate vdev") + return + } + csType := C.CString(string(vdev.Type)) + r := C.nvlist_add_string(child, C.sZPOOL_CONFIG_TYPE, + csType) + C.free(unsafe.Pointer(csType)) + if r != 0 { + err = errors.New("Failed to set vdev type") + return + } if vdev.Type == VDevTypeRaidz { r := C.nvlist_add_uint64(child, C.sZPOOL_CONFIG_NPARITY, @@ -715,49 +797,15 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, return } } - if err = buildVDevTree(child, vdev.Type, vdev.Devices, + if err = buildVDevTree(child, vdev.Type, vdev.Devices, nil, nil, props); err != nil { return } } else { - // if vdev.Type == VDevTypeDisk { - if r := C.nvlist_add_uint64(child, - C.sZPOOL_CONFIG_WHOLE_DISK, 1); r != 0 { - err = errors.New("Failed to allocate vdev child (whdisk)") + ashift, _ := strconv.Atoi(props[PoolPropAshift]) + if child, err = buildVdev(vdev, ashift); err != nil { return } - // } - if len(vdev.Path) > 0 { - csPath := C.CString(vdev.Path) - r := C.nvlist_add_string( - child, C.sZPOOL_CONFIG_PATH, - csPath) - C.free(unsafe.Pointer(csPath)) - if r != 0 { - err = errors.New("Failed to allocate vdev child (type)") - return - } - ashift, _ := strconv.Atoi(props[PoolPropAshift]) - if ashift > 0 { - if r := C.nvlist_add_uint64(child, - C.sZPOOL_CONFIG_ASHIFT, - C.uint64_t(ashift)); r != 0 { - err = errors.New("Failed to allocate vdev child (ashift)") - return - } - } - } - if vdev.Type == VDevTypeSpare { - C.nvlist_array_set(spares, C.int(nspares), child) - nspares++ - count-- - continue - } else if vdev.Type == VDevTypeL2cache { - C.nvlist_array_set(l2cache, C.int(nl2cache), child) - nl2cache++ - count-- - continue - } } C.nvlist_array_set(childrens, C.int(i), child) } @@ -768,31 +816,74 @@ func buildVDevTree(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, err = errors.New("Failed to allocate vdev children") return } - // fmt.Println("childs", root, count, rtype) - // debug.PrintStack() } - if nl2cache > 0 { - if r := C.nvlist_add_nvlist_array(root, - C.sZPOOL_CONFIG_L2CACHE, l2cache, - C.uint_t(nl2cache)); r != 0 { - err = errors.New("Failed to allocate vdev cache") + if len(spares) > 0 { + ashift, _ := strconv.Atoi(props[PoolPropAshift]) + if err = buildVdevSpares(root, VDevTypeRoot, spares, ashift); err != nil { return } } - if nspares > 0 { - if r := C.nvlist_add_nvlist_array(root, - C.sZPOOL_CONFIG_SPARES, spares, - C.uint_t(nspares)); r != 0 { - err = errors.New("Failed to allocate vdev spare") + if len(l2cache) > 0 { + ashift, _ := strconv.Atoi(props[PoolPropAshift]) + if err = buildVdevL2Cache(root, VDevTypeRoot, l2cache, ashift); err != nil { return } - // fmt.Println("spares", root, count) + } + return +} + +func buildVdevSpares(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, ashift int) (err error) { + count := len(vdevs) + if count == 0 { + return + } + spares := C.nvlist_alloc_array(C.int(count)) + if spares == nil { + err = errors.New("No enough memory buildVdevSpares") + return + } + defer C.nvlist_free_array(spares) + for i, vdev := range vdevs { + var child *C.struct_nvlist + if child, err = buildVdev(vdev, ashift); err != nil { + return + } + C.nvlist_array_set(spares, C.int(i), child) + } + if r := C.nvlist_add_nvlist_array(root, + C.sZPOOL_CONFIG_SPARES, spares, C.uint_t(len(vdevs))); r != 0 { + err = errors.New("Failed to allocate vdev spare") + } + return +} + +func buildVdevL2Cache(root *C.nvlist_t, rtype VDevType, vdevs []VDevTree, ashift int) (err error) { + count := len(vdevs) + if count == 0 { + return + } + l2cache := C.nvlist_alloc_array(C.int(count)) + if l2cache == nil { + err = errors.New("No enough memory buildVdevL2Cache") + return + } + defer C.nvlist_free_array(l2cache) + for i, vdev := range vdevs { + var child *C.struct_nvlist + if child, err = buildVdev(vdev, ashift); err != nil { + return + } + C.nvlist_array_set(l2cache, C.int(i), child) + } + if r := C.nvlist_add_nvlist_array(root, + C.sZPOOL_CONFIG_SPARES, l2cache, C.uint_t(len(vdevs))); r != 0 { + err = errors.New("Failed to allocate vdev l2cache") } return } // PoolCreate create ZFS pool per specs, features and properties of pool and root dataset -func PoolCreate(name string, vdevs []VDevTree, features map[string]string, +func PoolCreate(name string, vdev VDevTree, features map[string]string, props PoolProperties, fsprops DatasetProperties) (pool Pool, err error) { // create root vdev nvroot var nvroot *C.struct_nvlist @@ -811,7 +902,7 @@ func PoolCreate(name string, vdevs []VDevTree, features map[string]string, defer C.nvlist_free(nvroot) // Now we need to build specs (vdev hierarchy) - if err = buildVDevTree(nvroot, VDevTypeRoot, vdevs, props); err != nil { + if err = buildVDevTree(nvroot, VDevTypeRoot, vdev.Devices, vdev.Spares, vdev.L2Cache, props); err != nil { return } @@ -948,7 +1039,12 @@ func (pool *Pool) VDevTree() (vdevs VDevTree, err error) { if poolName, err = pool.Name(); err != nil { return } - return poolGetConfig(poolName, nvroot) + if vdevs, err = poolGetConfig(poolName, nvroot); err != nil { + return + } + vdevs.Spares, err = poolGetSpares(poolName, nvroot) + vdevs.L2Cache, err = poolGetL2Cache(poolName, nvroot) + return } func (s PoolState) String() string { diff --git a/zpool.h b/zpool.h index e29c24d..0961a36 100644 --- a/zpool.h +++ b/zpool.h @@ -56,6 +56,8 @@ const char *get_vdev_type(nvlist_ptr nv); const vdev_stat_ptr get_vdev_stats(nvlist_ptr nv); pool_scan_stat_ptr get_vdev_scan_stats(nvlist_t *nv); vdev_children_ptr get_vdev_children(nvlist_t *nv); +vdev_children_ptr get_vdev_spares(nvlist_t *nv); +vdev_children_ptr get_vdev_l2cache(nvlist_t *nv); const char *get_vdev_path(nvlist_ptr nv); uint64_t get_vdev_is_log(nvlist_ptr nv); diff --git a/zpool_test.go b/zpool_test.go index e257d30..8740d7b 100644 --- a/zpool_test.go +++ b/zpool_test.go @@ -84,6 +84,7 @@ func zpoolTestPoolCreate(t *testing.T) { disks := [2]string{s1path, s2path} + var vdev zfs.VDevTree var vdevs, mdevs, sdevs []zfs.VDevTree for _, d := range disks { mdevs = append(mdevs, @@ -93,8 +94,9 @@ func zpoolTestPoolCreate(t *testing.T) { {Type: zfs.VDevTypeFile, Path: s3path}} vdevs = []zfs.VDevTree{ zfs.VDevTree{Type: zfs.VDevTypeMirror, Devices: mdevs}, - zfs.VDevTree{Type: zfs.VDevTypeSpare, Devices: sdevs}, } + vdev.Devices = vdevs + vdev.Spares = sdevs props := make(map[zfs.Prop]string) fsprops := make(map[zfs.Prop]string) @@ -104,7 +106,7 @@ func zpoolTestPoolCreate(t *testing.T) { features["empty_bpobj"] = zfs.FENABLED features["lz4_compress"] = zfs.FENABLED - pool, err := zfs.PoolCreate(TSTPoolName, vdevs, features, props, fsprops) + pool, err := zfs.PoolCreate(TSTPoolName, vdev, features, props, fsprops) if err != nil { t.Error(err) // try cleanup @@ -231,6 +233,19 @@ func printVDevTree(vt zfs.VDevTree, pref string) { for _, v := range vt.Devices { printVDevTree(v, " "+pref) } + if len(vt.Spares) > 0 { + fmt.Println("spares:") + for _, v := range vt.Spares { + printVDevTree(v, " "+pref) + } + } + + if len(vt.L2Cache) > 0 { + fmt.Println("l2cache:") + for _, v := range vt.L2Cache { + printVDevTree(v, " "+pref) + } + } } func zpoolTestPoolImportSearch(t *testing.T) { @@ -249,7 +264,6 @@ func zpoolTestPoolImportSearch(t *testing.T) { fmt.Printf("%-30s | %-10s | %-10s | %s\n", "NAME", "TYPE", "STATE", "PATH") println("---------------------------------------------------------------") printVDevTree(p.VDevs, "") - } print("PASS\n\n") } @@ -408,6 +422,7 @@ func ExamplePoolOpenAll() { func ExamplePoolCreate() { disks := [2]string{"/dev/disk/by-id/ATA-123", "/dev/disk/by-id/ATA-456"} + var vdev zfs.VDevTree var vdevs, mdevs, sdevs []zfs.VDevTree // build mirror devices specs @@ -423,9 +438,11 @@ func ExamplePoolCreate() { // pool specs vdevs = []zfs.VDevTree{ zfs.VDevTree{Type: zfs.VDevTypeMirror, Devices: mdevs}, - zfs.VDevTree{Type: zfs.VDevTypeSpare, Devices: sdevs}, } + vdev.Devices = vdevs + vdev.Spares = sdevs + // pool properties props := make(map[zfs.Prop]string) // root dataset filesystem properties @@ -443,7 +460,7 @@ func ExamplePoolCreate() { // Based on specs formed above create test pool as 2 disk mirror and // one spare disk - pool, err := zfs.PoolCreate("TESTPOOL", vdevs, features, props, fsprops) + pool, err := zfs.PoolCreate("TESTPOOL", vdev, features, props, fsprops) if err != nil { println("Error: ", err.Error()) return