1*eda14cbcSMatt Macy /* 2*eda14cbcSMatt Macy * CDDL HEADER START 3*eda14cbcSMatt Macy * 4*eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5*eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6*eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7*eda14cbcSMatt Macy * 8*eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*eda14cbcSMatt Macy * or http://www.opensolaris.org/os/licensing. 10*eda14cbcSMatt Macy * See the License for the specific language governing permissions 11*eda14cbcSMatt Macy * and limitations under the License. 12*eda14cbcSMatt Macy * 13*eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14*eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16*eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17*eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18*eda14cbcSMatt Macy * 19*eda14cbcSMatt Macy * CDDL HEADER END 20*eda14cbcSMatt Macy */ 21*eda14cbcSMatt Macy /* 22*eda14cbcSMatt Macy * Copyright (c) 2018, 2019 by Delphix. All rights reserved. 23*eda14cbcSMatt Macy */ 24*eda14cbcSMatt Macy 25*eda14cbcSMatt Macy #include <sys/types.h> 26*eda14cbcSMatt Macy #include <sys/param.h> 27*eda14cbcSMatt Macy #include <sys/zfeature.h> 28*eda14cbcSMatt Macy #include <sys/zfs_ioctl.h> 29*eda14cbcSMatt Macy #include <sys/zfs_sysfs.h> 30*eda14cbcSMatt Macy #include <sys/kmem.h> 31*eda14cbcSMatt Macy #include <sys/fs/zfs.h> 32*eda14cbcSMatt Macy #include <linux/kobject.h> 33*eda14cbcSMatt Macy 34*eda14cbcSMatt Macy #include "zfs_prop.h" 35*eda14cbcSMatt Macy 36*eda14cbcSMatt Macy #if !defined(_KERNEL) 37*eda14cbcSMatt Macy #error kernel builds only 38*eda14cbcSMatt Macy #endif 39*eda14cbcSMatt Macy 40*eda14cbcSMatt Macy /* 41*eda14cbcSMatt Macy * ZFS Module sysfs support 42*eda14cbcSMatt Macy * 43*eda14cbcSMatt Macy * This extends our sysfs '/sys/module/zfs' entry to include feature 44*eda14cbcSMatt Macy * and property attributes. The primary consumer of this information 45*eda14cbcSMatt Macy * is user processes, like the zfs CLI, that need to know what the 46*eda14cbcSMatt Macy * current loaded ZFS module supports. The libzfs binary will consult 47*eda14cbcSMatt Macy * this information when instantiating the zfs|zpool property tables 48*eda14cbcSMatt Macy * and the pool features table. 49*eda14cbcSMatt Macy * 50*eda14cbcSMatt Macy * The added top-level directories are: 51*eda14cbcSMatt Macy * /sys/module/zfs 52*eda14cbcSMatt Macy * ├── features.kernel 53*eda14cbcSMatt Macy * ├── features.pool 54*eda14cbcSMatt Macy * ├── properties.dataset 55*eda14cbcSMatt Macy * └── properties.pool 56*eda14cbcSMatt Macy * 57*eda14cbcSMatt Macy * The local interface for the zfs kobjects includes: 58*eda14cbcSMatt Macy * zfs_kobj_init() 59*eda14cbcSMatt Macy * zfs_kobj_add() 60*eda14cbcSMatt Macy * zfs_kobj_release() 61*eda14cbcSMatt Macy * zfs_kobj_add_attr() 62*eda14cbcSMatt Macy * zfs_kobj_fini() 63*eda14cbcSMatt Macy */ 64*eda14cbcSMatt Macy 65*eda14cbcSMatt Macy /* 66*eda14cbcSMatt Macy * A zfs_mod_kobj_t represents a zfs kobject under '/sys/module/zfs' 67*eda14cbcSMatt Macy */ 68*eda14cbcSMatt Macy struct zfs_mod_kobj; 69*eda14cbcSMatt Macy typedef struct zfs_mod_kobj zfs_mod_kobj_t; 70*eda14cbcSMatt Macy 71*eda14cbcSMatt Macy struct zfs_mod_kobj { 72*eda14cbcSMatt Macy struct kobject zko_kobj; 73*eda14cbcSMatt Macy struct kobj_type zko_kobj_type; 74*eda14cbcSMatt Macy struct sysfs_ops zko_sysfs_ops; 75*eda14cbcSMatt Macy size_t zko_attr_count; 76*eda14cbcSMatt Macy struct attribute *zko_attr_list; /* allocated */ 77*eda14cbcSMatt Macy struct attribute **zko_default_attrs; /* allocated */ 78*eda14cbcSMatt Macy size_t zko_child_count; 79*eda14cbcSMatt Macy zfs_mod_kobj_t *zko_children; /* allocated */ 80*eda14cbcSMatt Macy }; 81*eda14cbcSMatt Macy 82*eda14cbcSMatt Macy #define ATTR_TABLE_SIZE(cnt) (sizeof (struct attribute) * (cnt)) 83*eda14cbcSMatt Macy /* Note +1 for NULL terminator slot */ 84*eda14cbcSMatt Macy #define DEFAULT_ATTR_SIZE(cnt) (sizeof (struct attribute *) * (cnt + 1)) 85*eda14cbcSMatt Macy #define CHILD_TABLE_SIZE(cnt) (sizeof (zfs_mod_kobj_t) * (cnt)) 86*eda14cbcSMatt Macy 87*eda14cbcSMatt Macy /* 88*eda14cbcSMatt Macy * These are the top-level kobjects under '/sys/module/zfs/' 89*eda14cbcSMatt Macy */ 90*eda14cbcSMatt Macy static zfs_mod_kobj_t kernel_features_kobj; 91*eda14cbcSMatt Macy static zfs_mod_kobj_t pool_features_kobj; 92*eda14cbcSMatt Macy static zfs_mod_kobj_t dataset_props_kobj; 93*eda14cbcSMatt Macy static zfs_mod_kobj_t pool_props_kobj; 94*eda14cbcSMatt Macy 95*eda14cbcSMatt Macy /* 96*eda14cbcSMatt Macy * The show function is used to provide the content 97*eda14cbcSMatt Macy * of an attribute into a PAGE_SIZE buffer. 98*eda14cbcSMatt Macy */ 99*eda14cbcSMatt Macy typedef ssize_t (*sysfs_show_func)(struct kobject *, struct attribute *, 100*eda14cbcSMatt Macy char *); 101*eda14cbcSMatt Macy 102*eda14cbcSMatt Macy static void 103*eda14cbcSMatt Macy zfs_kobj_fini(zfs_mod_kobj_t *zkobj) 104*eda14cbcSMatt Macy { 105*eda14cbcSMatt Macy /* finalize any child kobjects */ 106*eda14cbcSMatt Macy if (zkobj->zko_child_count != 0) { 107*eda14cbcSMatt Macy ASSERT(zkobj->zko_children); 108*eda14cbcSMatt Macy for (int i = 0; i < zkobj->zko_child_count; i++) 109*eda14cbcSMatt Macy zfs_kobj_fini(&zkobj->zko_children[i]); 110*eda14cbcSMatt Macy } 111*eda14cbcSMatt Macy 112*eda14cbcSMatt Macy /* kobject_put() will call zfs_kobj_release() to release memory */ 113*eda14cbcSMatt Macy kobject_del(&zkobj->zko_kobj); 114*eda14cbcSMatt Macy kobject_put(&zkobj->zko_kobj); 115*eda14cbcSMatt Macy } 116*eda14cbcSMatt Macy 117*eda14cbcSMatt Macy static void 118*eda14cbcSMatt Macy zfs_kobj_release(struct kobject *kobj) 119*eda14cbcSMatt Macy { 120*eda14cbcSMatt Macy zfs_mod_kobj_t *zkobj = container_of(kobj, zfs_mod_kobj_t, zko_kobj); 121*eda14cbcSMatt Macy 122*eda14cbcSMatt Macy if (zkobj->zko_attr_list != NULL) { 123*eda14cbcSMatt Macy ASSERT3S(zkobj->zko_attr_count, !=, 0); 124*eda14cbcSMatt Macy kmem_free(zkobj->zko_attr_list, 125*eda14cbcSMatt Macy ATTR_TABLE_SIZE(zkobj->zko_attr_count)); 126*eda14cbcSMatt Macy zkobj->zko_attr_list = NULL; 127*eda14cbcSMatt Macy } 128*eda14cbcSMatt Macy 129*eda14cbcSMatt Macy if (zkobj->zko_default_attrs != NULL) { 130*eda14cbcSMatt Macy kmem_free(zkobj->zko_default_attrs, 131*eda14cbcSMatt Macy DEFAULT_ATTR_SIZE(zkobj->zko_attr_count)); 132*eda14cbcSMatt Macy zkobj->zko_default_attrs = NULL; 133*eda14cbcSMatt Macy } 134*eda14cbcSMatt Macy 135*eda14cbcSMatt Macy if (zkobj->zko_child_count != 0) { 136*eda14cbcSMatt Macy ASSERT(zkobj->zko_children); 137*eda14cbcSMatt Macy 138*eda14cbcSMatt Macy kmem_free(zkobj->zko_children, 139*eda14cbcSMatt Macy CHILD_TABLE_SIZE(zkobj->zko_child_count)); 140*eda14cbcSMatt Macy zkobj->zko_child_count = 0; 141*eda14cbcSMatt Macy zkobj->zko_children = NULL; 142*eda14cbcSMatt Macy } 143*eda14cbcSMatt Macy 144*eda14cbcSMatt Macy zkobj->zko_attr_count = 0; 145*eda14cbcSMatt Macy } 146*eda14cbcSMatt Macy 147*eda14cbcSMatt Macy #ifndef sysfs_attr_init 148*eda14cbcSMatt Macy #define sysfs_attr_init(attr) do {} while (0) 149*eda14cbcSMatt Macy #endif 150*eda14cbcSMatt Macy 151*eda14cbcSMatt Macy static void 152*eda14cbcSMatt Macy zfs_kobj_add_attr(zfs_mod_kobj_t *zkobj, int attr_num, const char *attr_name) 153*eda14cbcSMatt Macy { 154*eda14cbcSMatt Macy VERIFY3U(attr_num, <, zkobj->zko_attr_count); 155*eda14cbcSMatt Macy ASSERT(zkobj->zko_attr_list); 156*eda14cbcSMatt Macy ASSERT(zkobj->zko_default_attrs); 157*eda14cbcSMatt Macy 158*eda14cbcSMatt Macy zkobj->zko_attr_list[attr_num].name = attr_name; 159*eda14cbcSMatt Macy zkobj->zko_attr_list[attr_num].mode = 0444; 160*eda14cbcSMatt Macy zkobj->zko_default_attrs[attr_num] = &zkobj->zko_attr_list[attr_num]; 161*eda14cbcSMatt Macy sysfs_attr_init(&zkobj->zko_attr_list[attr_num]); 162*eda14cbcSMatt Macy } 163*eda14cbcSMatt Macy 164*eda14cbcSMatt Macy static int 165*eda14cbcSMatt Macy zfs_kobj_init(zfs_mod_kobj_t *zkobj, int attr_cnt, int child_cnt, 166*eda14cbcSMatt Macy sysfs_show_func show_func) 167*eda14cbcSMatt Macy { 168*eda14cbcSMatt Macy /* 169*eda14cbcSMatt Macy * Initialize object's attributes. Count can be zero. 170*eda14cbcSMatt Macy */ 171*eda14cbcSMatt Macy if (attr_cnt > 0) { 172*eda14cbcSMatt Macy zkobj->zko_attr_list = kmem_zalloc(ATTR_TABLE_SIZE(attr_cnt), 173*eda14cbcSMatt Macy KM_SLEEP); 174*eda14cbcSMatt Macy if (zkobj->zko_attr_list == NULL) 175*eda14cbcSMatt Macy return (ENOMEM); 176*eda14cbcSMatt Macy } 177*eda14cbcSMatt Macy /* this will always have at least one slot for NULL termination */ 178*eda14cbcSMatt Macy zkobj->zko_default_attrs = kmem_zalloc(DEFAULT_ATTR_SIZE(attr_cnt), 179*eda14cbcSMatt Macy KM_SLEEP); 180*eda14cbcSMatt Macy if (zkobj->zko_default_attrs == NULL) { 181*eda14cbcSMatt Macy if (zkobj->zko_attr_list != NULL) { 182*eda14cbcSMatt Macy kmem_free(zkobj->zko_attr_list, 183*eda14cbcSMatt Macy ATTR_TABLE_SIZE(attr_cnt)); 184*eda14cbcSMatt Macy } 185*eda14cbcSMatt Macy return (ENOMEM); 186*eda14cbcSMatt Macy } 187*eda14cbcSMatt Macy zkobj->zko_attr_count = attr_cnt; 188*eda14cbcSMatt Macy zkobj->zko_kobj_type.default_attrs = zkobj->zko_default_attrs; 189*eda14cbcSMatt Macy 190*eda14cbcSMatt Macy if (child_cnt > 0) { 191*eda14cbcSMatt Macy zkobj->zko_children = kmem_zalloc(CHILD_TABLE_SIZE(child_cnt), 192*eda14cbcSMatt Macy KM_SLEEP); 193*eda14cbcSMatt Macy if (zkobj->zko_children == NULL) { 194*eda14cbcSMatt Macy if (zkobj->zko_default_attrs != NULL) { 195*eda14cbcSMatt Macy kmem_free(zkobj->zko_default_attrs, 196*eda14cbcSMatt Macy DEFAULT_ATTR_SIZE(attr_cnt)); 197*eda14cbcSMatt Macy } 198*eda14cbcSMatt Macy if (zkobj->zko_attr_list != NULL) { 199*eda14cbcSMatt Macy kmem_free(zkobj->zko_attr_list, 200*eda14cbcSMatt Macy ATTR_TABLE_SIZE(attr_cnt)); 201*eda14cbcSMatt Macy } 202*eda14cbcSMatt Macy return (ENOMEM); 203*eda14cbcSMatt Macy } 204*eda14cbcSMatt Macy zkobj->zko_child_count = child_cnt; 205*eda14cbcSMatt Macy } 206*eda14cbcSMatt Macy 207*eda14cbcSMatt Macy zkobj->zko_sysfs_ops.show = show_func; 208*eda14cbcSMatt Macy zkobj->zko_kobj_type.sysfs_ops = &zkobj->zko_sysfs_ops; 209*eda14cbcSMatt Macy zkobj->zko_kobj_type.release = zfs_kobj_release; 210*eda14cbcSMatt Macy 211*eda14cbcSMatt Macy return (0); 212*eda14cbcSMatt Macy } 213*eda14cbcSMatt Macy 214*eda14cbcSMatt Macy static int 215*eda14cbcSMatt Macy zfs_kobj_add(zfs_mod_kobj_t *zkobj, struct kobject *parent, const char *name) 216*eda14cbcSMatt Macy { 217*eda14cbcSMatt Macy /* zko_default_attrs must be NULL terminated */ 218*eda14cbcSMatt Macy ASSERT(zkobj->zko_default_attrs != NULL); 219*eda14cbcSMatt Macy ASSERT(zkobj->zko_default_attrs[zkobj->zko_attr_count] == NULL); 220*eda14cbcSMatt Macy 221*eda14cbcSMatt Macy kobject_init(&zkobj->zko_kobj, &zkobj->zko_kobj_type); 222*eda14cbcSMatt Macy return (kobject_add(&zkobj->zko_kobj, parent, name)); 223*eda14cbcSMatt Macy } 224*eda14cbcSMatt Macy 225*eda14cbcSMatt Macy /* 226*eda14cbcSMatt Macy * Each zfs property has these common attributes 227*eda14cbcSMatt Macy */ 228*eda14cbcSMatt Macy static const char *zprop_attrs[] = { 229*eda14cbcSMatt Macy "type", 230*eda14cbcSMatt Macy "readonly", 231*eda14cbcSMatt Macy "setonce", 232*eda14cbcSMatt Macy "visible", 233*eda14cbcSMatt Macy "values", 234*eda14cbcSMatt Macy "default", 235*eda14cbcSMatt Macy "datasets" /* zfs properties only */ 236*eda14cbcSMatt Macy }; 237*eda14cbcSMatt Macy 238*eda14cbcSMatt Macy #define ZFS_PROP_ATTR_COUNT ARRAY_SIZE(zprop_attrs) 239*eda14cbcSMatt Macy #define ZPOOL_PROP_ATTR_COUNT (ZFS_PROP_ATTR_COUNT - 1) 240*eda14cbcSMatt Macy 241*eda14cbcSMatt Macy static const char *zprop_types[] = { 242*eda14cbcSMatt Macy "number", 243*eda14cbcSMatt Macy "string", 244*eda14cbcSMatt Macy "index", 245*eda14cbcSMatt Macy }; 246*eda14cbcSMatt Macy 247*eda14cbcSMatt Macy typedef struct zfs_type_map { 248*eda14cbcSMatt Macy zfs_type_t ztm_type; 249*eda14cbcSMatt Macy const char *ztm_name; 250*eda14cbcSMatt Macy } zfs_type_map_t; 251*eda14cbcSMatt Macy 252*eda14cbcSMatt Macy static zfs_type_map_t type_map[] = { 253*eda14cbcSMatt Macy {ZFS_TYPE_FILESYSTEM, "filesystem"}, 254*eda14cbcSMatt Macy {ZFS_TYPE_SNAPSHOT, "snapshot"}, 255*eda14cbcSMatt Macy {ZFS_TYPE_VOLUME, "volume"}, 256*eda14cbcSMatt Macy {ZFS_TYPE_BOOKMARK, "bookmark"} 257*eda14cbcSMatt Macy }; 258*eda14cbcSMatt Macy 259*eda14cbcSMatt Macy /* 260*eda14cbcSMatt Macy * Show the content for a zfs property attribute 261*eda14cbcSMatt Macy */ 262*eda14cbcSMatt Macy static ssize_t 263*eda14cbcSMatt Macy zprop_sysfs_show(const char *attr_name, const zprop_desc_t *property, 264*eda14cbcSMatt Macy char *buf, size_t buflen) 265*eda14cbcSMatt Macy { 266*eda14cbcSMatt Macy const char *show_str; 267*eda14cbcSMatt Macy char number[32]; 268*eda14cbcSMatt Macy 269*eda14cbcSMatt Macy /* For dataset properties list the dataset types that apply */ 270*eda14cbcSMatt Macy if (strcmp(attr_name, "datasets") == 0 && 271*eda14cbcSMatt Macy property->pd_types != ZFS_TYPE_POOL) { 272*eda14cbcSMatt Macy int len = 0; 273*eda14cbcSMatt Macy 274*eda14cbcSMatt Macy for (int i = 0; i < ARRAY_SIZE(type_map); i++) { 275*eda14cbcSMatt Macy if (type_map[i].ztm_type & property->pd_types) { 276*eda14cbcSMatt Macy len += snprintf(buf + len, buflen - len, "%s ", 277*eda14cbcSMatt Macy type_map[i].ztm_name); 278*eda14cbcSMatt Macy } 279*eda14cbcSMatt Macy } 280*eda14cbcSMatt Macy len += snprintf(buf + len, buflen - len, "\n"); 281*eda14cbcSMatt Macy return (len); 282*eda14cbcSMatt Macy } 283*eda14cbcSMatt Macy 284*eda14cbcSMatt Macy if (strcmp(attr_name, "type") == 0) { 285*eda14cbcSMatt Macy show_str = zprop_types[property->pd_proptype]; 286*eda14cbcSMatt Macy } else if (strcmp(attr_name, "readonly") == 0) { 287*eda14cbcSMatt Macy show_str = property->pd_attr == PROP_READONLY ? "1" : "0"; 288*eda14cbcSMatt Macy } else if (strcmp(attr_name, "setonce") == 0) { 289*eda14cbcSMatt Macy show_str = property->pd_attr == PROP_ONETIME ? "1" : "0"; 290*eda14cbcSMatt Macy } else if (strcmp(attr_name, "visible") == 0) { 291*eda14cbcSMatt Macy show_str = property->pd_visible ? "1" : "0"; 292*eda14cbcSMatt Macy } else if (strcmp(attr_name, "values") == 0) { 293*eda14cbcSMatt Macy show_str = property->pd_values ? property->pd_values : ""; 294*eda14cbcSMatt Macy } else if (strcmp(attr_name, "default") == 0) { 295*eda14cbcSMatt Macy switch (property->pd_proptype) { 296*eda14cbcSMatt Macy case PROP_TYPE_NUMBER: 297*eda14cbcSMatt Macy (void) snprintf(number, sizeof (number), "%llu", 298*eda14cbcSMatt Macy (u_longlong_t)property->pd_numdefault); 299*eda14cbcSMatt Macy show_str = number; 300*eda14cbcSMatt Macy break; 301*eda14cbcSMatt Macy case PROP_TYPE_STRING: 302*eda14cbcSMatt Macy show_str = property->pd_strdefault ? 303*eda14cbcSMatt Macy property->pd_strdefault : ""; 304*eda14cbcSMatt Macy break; 305*eda14cbcSMatt Macy case PROP_TYPE_INDEX: 306*eda14cbcSMatt Macy if (zprop_index_to_string(property->pd_propnum, 307*eda14cbcSMatt Macy property->pd_numdefault, &show_str, 308*eda14cbcSMatt Macy property->pd_types) != 0) { 309*eda14cbcSMatt Macy show_str = ""; 310*eda14cbcSMatt Macy } 311*eda14cbcSMatt Macy break; 312*eda14cbcSMatt Macy default: 313*eda14cbcSMatt Macy return (0); 314*eda14cbcSMatt Macy } 315*eda14cbcSMatt Macy } else { 316*eda14cbcSMatt Macy return (0); 317*eda14cbcSMatt Macy } 318*eda14cbcSMatt Macy 319*eda14cbcSMatt Macy return (snprintf(buf, buflen, "%s\n", show_str)); 320*eda14cbcSMatt Macy } 321*eda14cbcSMatt Macy 322*eda14cbcSMatt Macy static ssize_t 323*eda14cbcSMatt Macy dataset_property_show(struct kobject *kobj, struct attribute *attr, char *buf) 324*eda14cbcSMatt Macy { 325*eda14cbcSMatt Macy zfs_prop_t prop = zfs_name_to_prop(kobject_name(kobj)); 326*eda14cbcSMatt Macy zprop_desc_t *prop_tbl = zfs_prop_get_table(); 327*eda14cbcSMatt Macy ssize_t len; 328*eda14cbcSMatt Macy 329*eda14cbcSMatt Macy ASSERT3U(prop, <, ZFS_NUM_PROPS); 330*eda14cbcSMatt Macy 331*eda14cbcSMatt Macy len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE); 332*eda14cbcSMatt Macy 333*eda14cbcSMatt Macy return (len); 334*eda14cbcSMatt Macy } 335*eda14cbcSMatt Macy 336*eda14cbcSMatt Macy static ssize_t 337*eda14cbcSMatt Macy pool_property_show(struct kobject *kobj, struct attribute *attr, char *buf) 338*eda14cbcSMatt Macy { 339*eda14cbcSMatt Macy zpool_prop_t prop = zpool_name_to_prop(kobject_name(kobj)); 340*eda14cbcSMatt Macy zprop_desc_t *prop_tbl = zpool_prop_get_table(); 341*eda14cbcSMatt Macy ssize_t len; 342*eda14cbcSMatt Macy 343*eda14cbcSMatt Macy ASSERT3U(prop, <, ZPOOL_NUM_PROPS); 344*eda14cbcSMatt Macy 345*eda14cbcSMatt Macy len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE); 346*eda14cbcSMatt Macy 347*eda14cbcSMatt Macy return (len); 348*eda14cbcSMatt Macy } 349*eda14cbcSMatt Macy 350*eda14cbcSMatt Macy /* 351*eda14cbcSMatt Macy * ZFS kernel feature attributes for '/sys/module/zfs/features.kernel' 352*eda14cbcSMatt Macy * 353*eda14cbcSMatt Macy * This list is intended for kernel features that don't have a pool feature 354*eda14cbcSMatt Macy * association or that extend existing user kernel interfaces. 355*eda14cbcSMatt Macy * 356*eda14cbcSMatt Macy * A user process can easily check if the running zfs kernel module 357*eda14cbcSMatt Macy * supports the new feature. 358*eda14cbcSMatt Macy */ 359*eda14cbcSMatt Macy static const char *zfs_kernel_features[] = { 360*eda14cbcSMatt Macy /* --> Add new kernel features here */ 361*eda14cbcSMatt Macy "com.delphix:vdev_initialize", 362*eda14cbcSMatt Macy "org.zfsonlinux:vdev_trim", 363*eda14cbcSMatt Macy "org.openzfs:l2arc_persistent", 364*eda14cbcSMatt Macy }; 365*eda14cbcSMatt Macy 366*eda14cbcSMatt Macy #define KERNEL_FEATURE_COUNT ARRAY_SIZE(zfs_kernel_features) 367*eda14cbcSMatt Macy 368*eda14cbcSMatt Macy static ssize_t 369*eda14cbcSMatt Macy kernel_feature_show(struct kobject *kobj, struct attribute *attr, char *buf) 370*eda14cbcSMatt Macy { 371*eda14cbcSMatt Macy if (strcmp(attr->name, "supported") == 0) 372*eda14cbcSMatt Macy return (snprintf(buf, PAGE_SIZE, "yes\n")); 373*eda14cbcSMatt Macy return (0); 374*eda14cbcSMatt Macy } 375*eda14cbcSMatt Macy 376*eda14cbcSMatt Macy static void 377*eda14cbcSMatt Macy kernel_feature_to_kobj(zfs_mod_kobj_t *parent, int slot, const char *name) 378*eda14cbcSMatt Macy { 379*eda14cbcSMatt Macy zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[slot]; 380*eda14cbcSMatt Macy 381*eda14cbcSMatt Macy ASSERT3U(slot, <, KERNEL_FEATURE_COUNT); 382*eda14cbcSMatt Macy ASSERT(name); 383*eda14cbcSMatt Macy 384*eda14cbcSMatt Macy int err = zfs_kobj_init(zfs_kobj, 1, 0, kernel_feature_show); 385*eda14cbcSMatt Macy if (err) 386*eda14cbcSMatt Macy return; 387*eda14cbcSMatt Macy 388*eda14cbcSMatt Macy zfs_kobj_add_attr(zfs_kobj, 0, "supported"); 389*eda14cbcSMatt Macy 390*eda14cbcSMatt Macy err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name); 391*eda14cbcSMatt Macy if (err) 392*eda14cbcSMatt Macy zfs_kobj_release(&zfs_kobj->zko_kobj); 393*eda14cbcSMatt Macy } 394*eda14cbcSMatt Macy 395*eda14cbcSMatt Macy static int 396*eda14cbcSMatt Macy zfs_kernel_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent) 397*eda14cbcSMatt Macy { 398*eda14cbcSMatt Macy /* 399*eda14cbcSMatt Macy * Create a parent kobject to host kernel features. 400*eda14cbcSMatt Macy * 401*eda14cbcSMatt Macy * '/sys/module/zfs/features.kernel' 402*eda14cbcSMatt Macy */ 403*eda14cbcSMatt Macy int err = zfs_kobj_init(zfs_kobj, 0, KERNEL_FEATURE_COUNT, 404*eda14cbcSMatt Macy kernel_feature_show); 405*eda14cbcSMatt Macy if (err) 406*eda14cbcSMatt Macy return (err); 407*eda14cbcSMatt Macy err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_KERNEL_FEATURES); 408*eda14cbcSMatt Macy if (err) { 409*eda14cbcSMatt Macy zfs_kobj_release(&zfs_kobj->zko_kobj); 410*eda14cbcSMatt Macy return (err); 411*eda14cbcSMatt Macy } 412*eda14cbcSMatt Macy 413*eda14cbcSMatt Macy /* 414*eda14cbcSMatt Macy * Now create a kobject for each feature. 415*eda14cbcSMatt Macy * 416*eda14cbcSMatt Macy * '/sys/module/zfs/features.kernel/<feature>' 417*eda14cbcSMatt Macy */ 418*eda14cbcSMatt Macy for (int f = 0; f < KERNEL_FEATURE_COUNT; f++) 419*eda14cbcSMatt Macy kernel_feature_to_kobj(zfs_kobj, f, zfs_kernel_features[f]); 420*eda14cbcSMatt Macy 421*eda14cbcSMatt Macy return (0); 422*eda14cbcSMatt Macy } 423*eda14cbcSMatt Macy 424*eda14cbcSMatt Macy /* 425*eda14cbcSMatt Macy * Each pool feature has these common attributes 426*eda14cbcSMatt Macy */ 427*eda14cbcSMatt Macy static const char *pool_feature_attrs[] = { 428*eda14cbcSMatt Macy "description", 429*eda14cbcSMatt Macy "guid", 430*eda14cbcSMatt Macy "uname", 431*eda14cbcSMatt Macy "readonly_compatible", 432*eda14cbcSMatt Macy "required_for_mos", 433*eda14cbcSMatt Macy "activate_on_enable", 434*eda14cbcSMatt Macy "per_dataset" 435*eda14cbcSMatt Macy }; 436*eda14cbcSMatt Macy 437*eda14cbcSMatt Macy #define ZPOOL_FEATURE_ATTR_COUNT ARRAY_SIZE(pool_feature_attrs) 438*eda14cbcSMatt Macy 439*eda14cbcSMatt Macy /* 440*eda14cbcSMatt Macy * Show the content for the given zfs pool feature attribute 441*eda14cbcSMatt Macy */ 442*eda14cbcSMatt Macy static ssize_t 443*eda14cbcSMatt Macy pool_feature_show(struct kobject *kobj, struct attribute *attr, char *buf) 444*eda14cbcSMatt Macy { 445*eda14cbcSMatt Macy spa_feature_t fid; 446*eda14cbcSMatt Macy 447*eda14cbcSMatt Macy if (zfeature_lookup_guid(kobject_name(kobj), &fid) != 0) 448*eda14cbcSMatt Macy return (0); 449*eda14cbcSMatt Macy 450*eda14cbcSMatt Macy ASSERT3U(fid, <, SPA_FEATURES); 451*eda14cbcSMatt Macy 452*eda14cbcSMatt Macy zfeature_flags_t flags = spa_feature_table[fid].fi_flags; 453*eda14cbcSMatt Macy const char *show_str = NULL; 454*eda14cbcSMatt Macy 455*eda14cbcSMatt Macy if (strcmp(attr->name, "description") == 0) { 456*eda14cbcSMatt Macy show_str = spa_feature_table[fid].fi_desc; 457*eda14cbcSMatt Macy } else if (strcmp(attr->name, "guid") == 0) { 458*eda14cbcSMatt Macy show_str = spa_feature_table[fid].fi_guid; 459*eda14cbcSMatt Macy } else if (strcmp(attr->name, "uname") == 0) { 460*eda14cbcSMatt Macy show_str = spa_feature_table[fid].fi_uname; 461*eda14cbcSMatt Macy } else if (strcmp(attr->name, "readonly_compatible") == 0) { 462*eda14cbcSMatt Macy show_str = flags & ZFEATURE_FLAG_READONLY_COMPAT ? "1" : "0"; 463*eda14cbcSMatt Macy } else if (strcmp(attr->name, "required_for_mos") == 0) { 464*eda14cbcSMatt Macy show_str = flags & ZFEATURE_FLAG_MOS ? "1" : "0"; 465*eda14cbcSMatt Macy } else if (strcmp(attr->name, "activate_on_enable") == 0) { 466*eda14cbcSMatt Macy show_str = flags & ZFEATURE_FLAG_ACTIVATE_ON_ENABLE ? "1" : "0"; 467*eda14cbcSMatt Macy } else if (strcmp(attr->name, "per_dataset") == 0) { 468*eda14cbcSMatt Macy show_str = flags & ZFEATURE_FLAG_PER_DATASET ? "1" : "0"; 469*eda14cbcSMatt Macy } 470*eda14cbcSMatt Macy if (show_str == NULL) 471*eda14cbcSMatt Macy return (0); 472*eda14cbcSMatt Macy 473*eda14cbcSMatt Macy return (snprintf(buf, PAGE_SIZE, "%s\n", show_str)); 474*eda14cbcSMatt Macy } 475*eda14cbcSMatt Macy 476*eda14cbcSMatt Macy static void 477*eda14cbcSMatt Macy pool_feature_to_kobj(zfs_mod_kobj_t *parent, spa_feature_t fid, 478*eda14cbcSMatt Macy const char *name) 479*eda14cbcSMatt Macy { 480*eda14cbcSMatt Macy zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[fid]; 481*eda14cbcSMatt Macy 482*eda14cbcSMatt Macy ASSERT3U(fid, <, SPA_FEATURES); 483*eda14cbcSMatt Macy ASSERT(name); 484*eda14cbcSMatt Macy 485*eda14cbcSMatt Macy int err = zfs_kobj_init(zfs_kobj, ZPOOL_FEATURE_ATTR_COUNT, 0, 486*eda14cbcSMatt Macy pool_feature_show); 487*eda14cbcSMatt Macy if (err) 488*eda14cbcSMatt Macy return; 489*eda14cbcSMatt Macy 490*eda14cbcSMatt Macy for (int i = 0; i < ZPOOL_FEATURE_ATTR_COUNT; i++) 491*eda14cbcSMatt Macy zfs_kobj_add_attr(zfs_kobj, i, pool_feature_attrs[i]); 492*eda14cbcSMatt Macy 493*eda14cbcSMatt Macy err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name); 494*eda14cbcSMatt Macy if (err) 495*eda14cbcSMatt Macy zfs_kobj_release(&zfs_kobj->zko_kobj); 496*eda14cbcSMatt Macy } 497*eda14cbcSMatt Macy 498*eda14cbcSMatt Macy static int 499*eda14cbcSMatt Macy zfs_pool_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent) 500*eda14cbcSMatt Macy { 501*eda14cbcSMatt Macy /* 502*eda14cbcSMatt Macy * Create a parent kobject to host pool features. 503*eda14cbcSMatt Macy * 504*eda14cbcSMatt Macy * '/sys/module/zfs/features.pool' 505*eda14cbcSMatt Macy */ 506*eda14cbcSMatt Macy int err = zfs_kobj_init(zfs_kobj, 0, SPA_FEATURES, pool_feature_show); 507*eda14cbcSMatt Macy if (err) 508*eda14cbcSMatt Macy return (err); 509*eda14cbcSMatt Macy err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_POOL_FEATURES); 510*eda14cbcSMatt Macy if (err) { 511*eda14cbcSMatt Macy zfs_kobj_release(&zfs_kobj->zko_kobj); 512*eda14cbcSMatt Macy return (err); 513*eda14cbcSMatt Macy } 514*eda14cbcSMatt Macy 515*eda14cbcSMatt Macy /* 516*eda14cbcSMatt Macy * Now create a kobject for each feature. 517*eda14cbcSMatt Macy * 518*eda14cbcSMatt Macy * '/sys/module/zfs/features.pool/<feature>' 519*eda14cbcSMatt Macy */ 520*eda14cbcSMatt Macy for (spa_feature_t i = 0; i < SPA_FEATURES; i++) 521*eda14cbcSMatt Macy pool_feature_to_kobj(zfs_kobj, i, spa_feature_table[i].fi_guid); 522*eda14cbcSMatt Macy 523*eda14cbcSMatt Macy return (0); 524*eda14cbcSMatt Macy } 525*eda14cbcSMatt Macy 526*eda14cbcSMatt Macy typedef struct prop_to_kobj_arg { 527*eda14cbcSMatt Macy zprop_desc_t *p2k_table; 528*eda14cbcSMatt Macy zfs_mod_kobj_t *p2k_parent; 529*eda14cbcSMatt Macy sysfs_show_func p2k_show_func; 530*eda14cbcSMatt Macy int p2k_attr_count; 531*eda14cbcSMatt Macy } prop_to_kobj_arg_t; 532*eda14cbcSMatt Macy 533*eda14cbcSMatt Macy static int 534*eda14cbcSMatt Macy zprop_to_kobj(int prop, void *args) 535*eda14cbcSMatt Macy { 536*eda14cbcSMatt Macy prop_to_kobj_arg_t *data = args; 537*eda14cbcSMatt Macy zfs_mod_kobj_t *parent = data->p2k_parent; 538*eda14cbcSMatt Macy zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[prop]; 539*eda14cbcSMatt Macy const char *name = data->p2k_table[prop].pd_name; 540*eda14cbcSMatt Macy int err; 541*eda14cbcSMatt Macy 542*eda14cbcSMatt Macy ASSERT(name); 543*eda14cbcSMatt Macy 544*eda14cbcSMatt Macy err = zfs_kobj_init(zfs_kobj, data->p2k_attr_count, 0, 545*eda14cbcSMatt Macy data->p2k_show_func); 546*eda14cbcSMatt Macy if (err) 547*eda14cbcSMatt Macy return (ZPROP_CONT); 548*eda14cbcSMatt Macy 549*eda14cbcSMatt Macy for (int i = 0; i < data->p2k_attr_count; i++) 550*eda14cbcSMatt Macy zfs_kobj_add_attr(zfs_kobj, i, zprop_attrs[i]); 551*eda14cbcSMatt Macy 552*eda14cbcSMatt Macy err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name); 553*eda14cbcSMatt Macy if (err) 554*eda14cbcSMatt Macy zfs_kobj_release(&zfs_kobj->zko_kobj); 555*eda14cbcSMatt Macy 556*eda14cbcSMatt Macy return (ZPROP_CONT); 557*eda14cbcSMatt Macy } 558*eda14cbcSMatt Macy 559*eda14cbcSMatt Macy static int 560*eda14cbcSMatt Macy zfs_sysfs_properties_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent, 561*eda14cbcSMatt Macy zfs_type_t type) 562*eda14cbcSMatt Macy { 563*eda14cbcSMatt Macy prop_to_kobj_arg_t context; 564*eda14cbcSMatt Macy const char *name; 565*eda14cbcSMatt Macy int err; 566*eda14cbcSMatt Macy 567*eda14cbcSMatt Macy /* 568*eda14cbcSMatt Macy * Create a parent kobject to host properties. 569*eda14cbcSMatt Macy * 570*eda14cbcSMatt Macy * '/sys/module/zfs/properties.<type>' 571*eda14cbcSMatt Macy */ 572*eda14cbcSMatt Macy if (type == ZFS_TYPE_POOL) { 573*eda14cbcSMatt Macy name = ZFS_SYSFS_POOL_PROPERTIES; 574*eda14cbcSMatt Macy context.p2k_table = zpool_prop_get_table(); 575*eda14cbcSMatt Macy context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT; 576*eda14cbcSMatt Macy context.p2k_parent = zfs_kobj; 577*eda14cbcSMatt Macy context.p2k_show_func = pool_property_show; 578*eda14cbcSMatt Macy err = zfs_kobj_init(zfs_kobj, 0, ZPOOL_NUM_PROPS, 579*eda14cbcSMatt Macy pool_property_show); 580*eda14cbcSMatt Macy } else { 581*eda14cbcSMatt Macy name = ZFS_SYSFS_DATASET_PROPERTIES; 582*eda14cbcSMatt Macy context.p2k_table = zfs_prop_get_table(); 583*eda14cbcSMatt Macy context.p2k_attr_count = ZFS_PROP_ATTR_COUNT; 584*eda14cbcSMatt Macy context.p2k_parent = zfs_kobj; 585*eda14cbcSMatt Macy context.p2k_show_func = dataset_property_show; 586*eda14cbcSMatt Macy err = zfs_kobj_init(zfs_kobj, 0, ZFS_NUM_PROPS, 587*eda14cbcSMatt Macy dataset_property_show); 588*eda14cbcSMatt Macy } 589*eda14cbcSMatt Macy 590*eda14cbcSMatt Macy if (err) 591*eda14cbcSMatt Macy return (err); 592*eda14cbcSMatt Macy 593*eda14cbcSMatt Macy err = zfs_kobj_add(zfs_kobj, parent, name); 594*eda14cbcSMatt Macy if (err) { 595*eda14cbcSMatt Macy zfs_kobj_release(&zfs_kobj->zko_kobj); 596*eda14cbcSMatt Macy return (err); 597*eda14cbcSMatt Macy } 598*eda14cbcSMatt Macy 599*eda14cbcSMatt Macy /* 600*eda14cbcSMatt Macy * Create a kobject for each property. 601*eda14cbcSMatt Macy * 602*eda14cbcSMatt Macy * '/sys/module/zfs/properties.<type>/<property>' 603*eda14cbcSMatt Macy */ 604*eda14cbcSMatt Macy (void) zprop_iter_common(zprop_to_kobj, &context, B_TRUE, 605*eda14cbcSMatt Macy B_FALSE, type); 606*eda14cbcSMatt Macy 607*eda14cbcSMatt Macy return (err); 608*eda14cbcSMatt Macy } 609*eda14cbcSMatt Macy 610*eda14cbcSMatt Macy void 611*eda14cbcSMatt Macy zfs_sysfs_init(void) 612*eda14cbcSMatt Macy { 613*eda14cbcSMatt Macy struct kobject *parent; 614*eda14cbcSMatt Macy #if defined(CONFIG_ZFS) && !defined(CONFIG_ZFS_MODULE) 615*eda14cbcSMatt Macy parent = kobject_create_and_add("zfs", fs_kobj); 616*eda14cbcSMatt Macy #else 617*eda14cbcSMatt Macy parent = &(((struct module *)(THIS_MODULE))->mkobj).kobj; 618*eda14cbcSMatt Macy #endif 619*eda14cbcSMatt Macy int err; 620*eda14cbcSMatt Macy 621*eda14cbcSMatt Macy if (parent == NULL) 622*eda14cbcSMatt Macy return; 623*eda14cbcSMatt Macy 624*eda14cbcSMatt Macy err = zfs_kernel_features_init(&kernel_features_kobj, parent); 625*eda14cbcSMatt Macy if (err) 626*eda14cbcSMatt Macy return; 627*eda14cbcSMatt Macy 628*eda14cbcSMatt Macy err = zfs_pool_features_init(&pool_features_kobj, parent); 629*eda14cbcSMatt Macy if (err) { 630*eda14cbcSMatt Macy zfs_kobj_fini(&kernel_features_kobj); 631*eda14cbcSMatt Macy return; 632*eda14cbcSMatt Macy } 633*eda14cbcSMatt Macy 634*eda14cbcSMatt Macy err = zfs_sysfs_properties_init(&pool_props_kobj, parent, 635*eda14cbcSMatt Macy ZFS_TYPE_POOL); 636*eda14cbcSMatt Macy if (err) { 637*eda14cbcSMatt Macy zfs_kobj_fini(&kernel_features_kobj); 638*eda14cbcSMatt Macy zfs_kobj_fini(&pool_features_kobj); 639*eda14cbcSMatt Macy return; 640*eda14cbcSMatt Macy } 641*eda14cbcSMatt Macy 642*eda14cbcSMatt Macy err = zfs_sysfs_properties_init(&dataset_props_kobj, parent, 643*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM); 644*eda14cbcSMatt Macy if (err) { 645*eda14cbcSMatt Macy zfs_kobj_fini(&kernel_features_kobj); 646*eda14cbcSMatt Macy zfs_kobj_fini(&pool_features_kobj); 647*eda14cbcSMatt Macy zfs_kobj_fini(&pool_props_kobj); 648*eda14cbcSMatt Macy return; 649*eda14cbcSMatt Macy } 650*eda14cbcSMatt Macy } 651*eda14cbcSMatt Macy 652*eda14cbcSMatt Macy void 653*eda14cbcSMatt Macy zfs_sysfs_fini(void) 654*eda14cbcSMatt Macy { 655*eda14cbcSMatt Macy /* 656*eda14cbcSMatt Macy * Remove top-level kobjects; each will remove any children kobjects 657*eda14cbcSMatt Macy */ 658*eda14cbcSMatt Macy zfs_kobj_fini(&kernel_features_kobj); 659*eda14cbcSMatt Macy zfs_kobj_fini(&pool_features_kobj); 660*eda14cbcSMatt Macy zfs_kobj_fini(&dataset_props_kobj); 661*eda14cbcSMatt Macy zfs_kobj_fini(&pool_props_kobj); 662*eda14cbcSMatt Macy } 663