/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015, 2016 Joyent, Inc. All rights reserved. * Copyright (c) 2014, 2017 by Delphix. All rights reserved. */ #ifndef _SYS_SDEV_IMPL_H #define _SYS_SDEV_IMPL_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include /* * sdev_nodes are the file-system specific part of the * vnodes for the device filesystem. * * The device filesystem exports two node types: * * VDIR nodes to represent directories * VCHR & VBLK nodes to represent devices */ /* * /dev mount arguments */ struct sdev_mountargs { uint64_t sdev_attrdir; }; /* * Nvpair names of profile information (list of device files available) of * non-global /dev mounts. These strings must be unique among them. */ #define SDEV_NVNAME_MOUNTPT "prof_mountpt" #define SDEV_NVNAME_INCLUDE "prof_include" #define SDEV_NVNAME_EXCLUDE "prof_exclude" #define SDEV_NVNAME_SYMLINK "prof_symlink" #define SDEV_NVNAME_MAP "prof_map" /* * supported devfsadm_cmd */ #define DEVFSADMD_RUN_ALL 1 /* * devfsadm_error codes */ #define DEVFSADM_RUN_INVALID 1 #define DEVFSADM_RUN_EPERM 2 #define DEVFSADM_RUN_NOTSUP 3 /* * devfsadm/devname door data structures */ typedef struct sdev_door_arg { uint8_t devfsadm_cmd; /* what to do for devfsadm[d] */ } sdev_door_arg_t; typedef struct sdev_door_res { int32_t devfsadm_error; } sdev_door_res_t; #ifdef _KERNEL struct sdev_dprof { int has_glob; nvlist_t *dev_name; nvlist_t *dev_map; nvlist_t *dev_symlink; nvlist_t *dev_glob_incdir; nvlist_t *dev_glob_excdir; }; /* * devname_handle_t */ struct devname_handle { struct sdev_node *dh_data; /* the sdev_node */ void *dh_args; }; typedef struct devname_handle devname_handle_t; /* * Per-instance node data for the global zone instance * Only one mount of /dev in the global zone */ typedef struct sdev_global_data { struct devname_handle sdev_ghandle; ulong_t sdev_dir_ggen; /* name space generation # */ } sdev_global_data_t; /* * Per-instance node data - profile data per non-global zone mount instance */ typedef struct sdev_local_data { ulong_t sdev_dir_lgen; /* cached generation # of /dev dir */ ulong_t sdev_devtree_lgen; /* cached generation # of devtree */ struct sdev_node *sdev_lorigin; /* corresponding global sdev_node */ struct sdev_dprof sdev_lprof; /* profile for multi-inst */ } sdev_local_data_t; /* sdev_flags */ typedef enum sdev_flags { SDEV_BUILD = 0x0001, /* directory cache out-of-date */ SDEV_GLOBAL = 0x0002, /* global /dev nodes */ SDEV_PERSIST = 0x0004, /* backing store persisted node */ SDEV_NO_NCACHE = 0x0008, /* do not include in neg. cache */ SDEV_DYNAMIC = 0x0010, /* special-purpose vnode ops */ /* (ex: pts) */ SDEV_VTOR = 0x0020, /* validate sdev_nodes during search */ SDEV_ATTR_INVALID = 0x0040, /* invalid node attributes, */ /* need update */ SDEV_SUBDIR = 0x0080, /* match all subdirs under here */ SDEV_ZONED = 0x0100 /* zoned subdir */ } sdev_flags_t; /* * /dev filesystem sdev_node defines */ typedef struct sdev_node { char *sdev_name; /* node name */ size_t sdev_namelen; /* strlen(sdev_name) */ char *sdev_path; /* absolute path */ char *sdev_symlink; /* source for a symlink */ struct vnode *sdev_vnode; /* vnode */ krwlock_t sdev_contents; /* rw lock for this data structure */ struct sdev_node *sdev_dotdot; /* parent */ avl_tree_t sdev_entries; /* VDIR: contents as avl tree */ avl_node_t sdev_avllink; /* avl node linkage */ struct vnode *sdev_attrvp; /* backing store vnode if persisted */ struct vattr *sdev_attr; /* memory copy of the vattr */ ino64_t sdev_ino; /* inode */ uint_t sdev_nlink; /* link count */ int sdev_state; /* state of this node */ sdev_flags_t sdev_flags; /* flags bit */ kmutex_t sdev_lookup_lock; /* node creation synch lock */ kcondvar_t sdev_lookup_cv; /* node creation sync cv */ int sdev_lookup_flags; /* node creation flags */ /* per-instance data, either global or non-global zone */ union { struct sdev_global_data sdev_globaldata; struct sdev_local_data sdev_localdata; } sdev_instance_data; list_node_t sdev_plist; /* link on plugin list */ void *sdev_private; } sdev_node_t; #define sdev_ldata sdev_instance_data.sdev_localdata #define sdev_gdata sdev_instance_data.sdev_globaldata #define sdev_handle sdev_gdata.sdev_ghandle #define sdev_gdir_gen sdev_gdata.sdev_dir_ggen #define sdev_ldir_gen sdev_ldata.sdev_dir_lgen #define sdev_devtree_gen sdev_ldata.sdev_devtree_lgen #define sdev_origin sdev_ldata.sdev_lorigin #define sdev_prof sdev_ldata.sdev_lprof /* * Directory contents traversal */ #define SDEV_FIRST_ENTRY(ddv) avl_first(&(ddv)->sdev_entries) #define SDEV_NEXT_ENTRY(ddv, dv) AVL_NEXT(&(ddv)->sdev_entries, (dv)) /* * See the big theory statement in sdev_vnops.c for an explanation of these * states. */ typedef enum { SDEV_ZOMBIE = -1, SDEV_INIT = 0, SDEV_READY } sdev_node_state_t; /* sdev_lookup_flags */ #define SDEV_LOOKUP 0x0001 /* node creation in progress */ #define SDEV_READDIR 0x0002 /* VDIR readdir in progress */ #define SDEV_LGWAITING 0x0004 /* waiting for devfsadm completion */ /* convenient macros */ #define SDEV_IS_GLOBAL(dv) \ (dv->sdev_flags & SDEV_GLOBAL) #define SDEV_IS_PERSIST(dv) \ (dv->sdev_flags & SDEV_PERSIST) #define SDEV_IS_DYNAMIC(dv) \ (dv->sdev_flags & SDEV_DYNAMIC) #define SDEV_IS_NO_NCACHE(dv) \ (dv->sdev_flags & SDEV_NO_NCACHE) #define SDEV_IS_LOOKUP(dv) \ (dv->sdev_lookup_flags & SDEV_LOOKUP) #define SDEV_IS_READDIR(dv) \ (dv->sdev_lookup_flags & SDEV_READDIR) #define SDEV_IS_LGWAITING(dv) \ (dv->sdev_lookup_flags & SDEV_LGWAITING) #define SDEVTOV(n) ((struct vnode *)(n)->sdev_vnode) #define VTOSDEV(vp) ((struct sdev_node *)(vp)->v_data) #define VN_HELD(v) ((v)->v_count != 0) #define SDEV_HELD(dv) (VN_HELD(SDEVTOV(dv))) #define SDEV_HOLD(dv) VN_HOLD(SDEVTOV(dv)) #define SDEV_RELE(dv) VN_RELE(SDEVTOV(dv)) #define SDEV_SIMPLE_RELE(dv) { \ struct vnode *vp = SDEVTOV(dv); \ mutex_enter(&vp->v_lock); \ VN_RELE_LOCKED(vp); \ mutex_exit(&vp->v_lock); \ } #define SDEV_ACL_FLAVOR(vp) (VFSTOSDEVFS(vp->v_vfsp)->sdev_acl_flavor) /* * some defaults */ #define SDEV_ROOTINO ((ino_t)2) #define SDEV_UID_DEFAULT (0) #define SDEV_GID_DEFAULT (3) #define SDEV_DIRMODE_DEFAULT (S_IFDIR |0755) #define SDEV_DEVMODE_DEFAULT (0600) #define SDEV_LNKMODE_DEFAULT (S_IFLNK | 0777) extern struct vattr sdev_vattr_dir; extern struct vattr sdev_vattr_lnk; extern struct vattr sdev_vattr_blk; extern struct vattr sdev_vattr_chr; /* * devname_lookup_func() */ extern int devname_lookup_func(struct sdev_node *, char *, struct vnode **, struct cred *, int (*)(struct sdev_node *, char *, void **, struct cred *, void *, char *), int); /* * flags used by devname_lookup_func callbacks */ #define SDEV_VATTR 0x4 /* callback returning node vattr */ #define SDEV_VLINK 0x8 /* callback returning /dev link */ /* * devname_readdir_func() */ extern int devname_readdir_func(vnode_t *, uio_t *, cred_t *, int *, int); /* * flags for devname_readdir_func */ #define SDEV_BROWSE 0x1 /* fetch all entries from backing store */ /* * devname_setattr_func() */ extern int devname_setattr_func(struct vnode *, struct vattr *, int, struct cred *, int (*)(struct sdev_node *, struct vattr *, int), int); /* * devname_inactive_func() */ extern void devname_inactive_func(struct vnode *, struct cred *, void (*)(struct vnode *)); /* * /dev file system instance defines */ /* * /dev version of vfs_data */ struct sdev_data { struct sdev_data *sdev_prev; struct sdev_data *sdev_next; struct sdev_node *sdev_root; struct vfs *sdev_vfsp; struct sdev_mountargs *sdev_mountargs; ulong_t sdev_acl_flavor; }; #define VFSTOSDEVFS(vfsp) ((struct sdev_data *)((vfsp)->vfs_data)) /* * sdev_fid overlays the fid structure (for VFS_VGET) */ struct sdev_fid { uint16_t sdevfid_len; ino32_t sdevfid_ino; int32_t sdevfid_gen; }; /* * devfsadm and devname communication defines */ typedef enum { DEVNAME_DEVFSADM_STOPPED = 0, /* devfsadm has never run */ DEVNAME_DEVFSADM_RUNNING, /* devfsadm is running */ DEVNAME_DEVFSADM_RUN /* devfsadm ran once */ } devname_devfsadm_state_t; extern volatile uint_t devfsadm_state; /* atomic mask for devfsadm status */ #define DEVNAME_DEVFSADM_SET_RUNNING(devfsadm_state) \ (devfsadm_state = DEVNAME_DEVFSADM_RUNNING) #define DEVNAME_DEVFSADM_SET_STOP(devfsadm_state) \ (devfsadm_state = DEVNAME_DEVFSADM_STOPPED) #define DEVNAME_DEVFSADM_SET_RUN(devfsadm_state) \ (devfsadm_state = DEVNAME_DEVFSADM_RUN) #define DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) \ (devfsadm_state == DEVNAME_DEVFSADM_RUNNING) #define DEVNAME_DEVFSADM_HAS_RUN(devfsadm_state) \ (devfsadm_state == DEVNAME_DEVFSADM_RUN) #define SDEV_BLOCK_OTHERS(dv, cmd) { \ ASSERT(MUTEX_HELD(&dv->sdev_lookup_lock)); \ dv->sdev_lookup_flags |= cmd; \ } extern void sdev_unblock_others(struct sdev_node *, uint_t); #define SDEV_UNBLOCK_OTHERS(dv, cmd) { \ sdev_unblock_others(dv, cmd); \ } #define SDEV_CLEAR_LOOKUP_FLAGS(dv, cmd) { \ dv->sdev_lookup_flags &= ~cmd; \ } extern int sdev_wait4lookup(struct sdev_node *, int); extern int devname_filename_register(char *); extern int devname_nsmaps_register(char *, size_t); extern void sdev_devfsadm_lockinit(void); extern void sdev_devfsadm_lockdestroy(void); extern void devname_add_devfsadm_node(char *); extern void sdev_devfsadmd_thread(struct sdev_node *, struct sdev_node *, struct cred *); extern int devname_profile_update(char *, size_t); extern struct sdev_data *sdev_find_mntinfo(char *); void sdev_mntinfo_rele(struct sdev_data *); typedef void (*sdev_mnt_walk_f)(struct sdev_node *, void *); void sdev_mnt_walk(sdev_mnt_walk_f, void *); extern struct vnodeops *devpts_getvnodeops(void); extern struct vnodeops *devvt_getvnodeops(void); extern void sdev_plugin_nodeready(struct sdev_node *); extern int sdev_plugin_init(void); extern int sdev_plugin_fini(void); /* * boot states - warning, the ordering here is significant * * the difference between "system available" and "boot complete" * is a debounce timeout to catch some daemon issuing a readdir * triggering a nuisance implict reconfig on each boot. */ #define SDEV_BOOT_STATE_INITIAL 0 #define SDEV_BOOT_STATE_RECONFIG 1 /* reconfig */ #define SDEV_BOOT_STATE_SYSAVAIL 2 /* system available */ #define SDEV_BOOT_STATE_COMPLETE 3 /* boot complete */ /* * Negative cache list and list element * The mutex protects the flags against multiple accesses and * must only be acquired when already holding the r/w lock. */ typedef struct sdev_nc_list { list_t ncl_list; /* the list itself */ kmutex_t ncl_mutex; /* protects ncl_flags */ krwlock_t ncl_lock; /* protects ncl_list */ int ncl_flags; int ncl_nentries; } sdev_nc_list_t; typedef struct sdev_nc_node { char *ncn_name; /* name of the node */ int ncn_flags; /* state information */ int ncn_expirecnt; /* remove once expired */ list_node_t ncn_link; /* link to next in list */ } sdev_nc_node_t; /* ncl_flags */ #define NCL_LIST_DIRTY 0x01 /* needs to be flushed */ #define NCL_LIST_WRITING 0x02 /* write in progress */ #define NCL_LIST_WENABLE 0x04 /* write-enabled post boot */ /* ncn_flags */ #define NCN_ACTIVE 0x01 /* a lookup has occurred */ #define NCN_SRC_STORE 0x02 /* src: persistent store */ #define NCN_SRC_CURRENT 0x04 /* src: current boot */ /* sdev_lookup_failed flags */ #define SLF_NO_NCACHE 0x01 /* node should not be added to ncache */ #define SLF_REBUILT 0x02 /* reconfig performed during lookup attempt */ /* * The nvlist name and nvpair identifiers in the * /etc/devices/devname_cache nvlist format */ #define DP_DEVNAME_ID "devname" #define DP_DEVNAME_NCACHE_ID "ncache" #define DP_DEVNAME_NC_EXPIRECNT_ID "expire-counts" /* devname-cache list element */ typedef struct nvp_devname { char **nvp_paths; int *nvp_expirecnts; int nvp_npaths; list_node_t nvp_link; } nvp_devname_t; /* * name service globals and prototypes */ /* * vnodeops and vfsops helpers */ typedef enum { SDEV_CACHE_ADD = 0, SDEV_CACHE_DELETE } sdev_cache_ops_t; extern struct sdev_node *sdev_cache_lookup(struct sdev_node *, char *); extern void sdev_cache_update(struct sdev_node *, struct sdev_node **, char *, sdev_cache_ops_t); extern void sdev_node_cache_init(void); extern void sdev_node_cache_fini(void); extern struct sdev_node *sdev_mkroot(struct vfs *, dev_t, struct vnode *, struct vnode *, struct cred *); extern void sdev_filldir_dynamic(struct sdev_node *); extern int sdev_mknode(struct sdev_node *, char *, struct sdev_node **, struct vattr *, struct vnode *, void *, struct cred *, sdev_node_state_t); extern int sdev_getlink(struct vnode *linkvp, char **link); extern int sdev_nodeinit(struct sdev_node *, char *, struct sdev_node **, vattr_t *); extern int sdev_nodeready(struct sdev_node *, vattr_t *, vnode_t *, void *, cred_t *); extern int sdev_shadow_node(struct sdev_node *, struct cred *); extern void sdev_nodedestroy(struct sdev_node *, uint_t); extern void sdev_update_timestamps(struct vnode *, cred_t *, uint_t); extern void sdev_vattr_merge(struct sdev_node *, struct vattr *); extern void sdev_devstate_change(void); extern int sdev_lookup_filter(sdev_node_t *, char *); extern void sdev_lookup_failed(sdev_node_t *, char *, int); extern int sdev_unlocked_access(void *, int, struct cred *); #define SDEV_ENFORCE 0x1 extern void sdev_stale(struct sdev_node *); extern int sdev_cleandir(struct sdev_node *, char *, uint_t); extern int sdev_rnmnode(struct sdev_node *, struct sdev_node *, struct sdev_node *, struct sdev_node **, char *, struct cred *); extern size_t add_dir_entry(dirent64_t *, char *, size_t, ino_t, offset_t); extern struct vattr *sdev_getdefault_attr(enum vtype type); extern int sdev_to_vp(struct sdev_node *, struct vnode **); extern ino_t sdev_mkino(struct sdev_node *); extern int devname_backstore_lookup(struct sdev_node *, char *, struct vnode **); extern int sdev_is_devfs_node(char *); extern int sdev_copyin_mountargs(struct mounta *, struct sdev_mountargs *); extern int sdev_reserve_subdirs(struct sdev_node *); extern int prof_lookup(); extern void prof_filldir(struct sdev_node *); extern int prof_name_matched(char *, struct sdev_node *); extern int devpts_validate(struct sdev_node *dv); extern int devnet_validate(struct sdev_node *dv); extern int devipnet_validate(struct sdev_node *dv); extern int devvt_validate(struct sdev_node *dv); extern int devzvol_validate(struct sdev_node *dv); extern void *sdev_get_vtor(struct sdev_node *dv); /* * devinfo helpers */ extern int sdev_modctl_readdir(const char *, char ***, int *, int *, int); extern void sdev_modctl_readdir_free(char **, int, int); extern int sdev_modctl_devexists(const char *); /* * ncache handlers */ extern void sdev_ncache_init(void); extern void sdev_ncache_setup(void); extern void sdev_ncache_teardown(void); extern void sdev_nc_addname(sdev_nc_list_t *, sdev_node_t *, char *, int); extern void sdev_nc_node_exists(sdev_node_t *); extern void sdev_nc_path_exists(sdev_nc_list_t *, char *); extern void sdev_modctl_dump_files(void); /* * plugin and legacy vtab stuff */ /* directory dependent vop table */ typedef struct sdev_vop_table { char *vt_name; /* subdirectory name */ const fs_operation_def_t *vt_service; /* vnodeops table */ struct vnodeops **vt_global_vops; /* global container for vop */ int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */ int vt_flags; } sdev_vop_table_t; extern struct sdev_vop_table vtab[]; extern struct vnodeops *sdev_get_vop(struct sdev_node *); extern void sdev_set_no_negcache(struct sdev_node *); extern void *sdev_get_vtor(struct sdev_node *dv); /* * globals */ extern kmutex_t sdev_lock; extern int devtype; extern kmem_cache_t *sdev_node_cache; extern struct vnodeops *sdev_vnodeops; extern struct vnodeops *devpts_vnodeops; extern struct vnodeops *devnet_vnodeops; extern struct vnodeops *devipnet_vnodeops; extern struct vnodeops *devvt_vnodeops; extern struct sdev_data *sdev_origins; /* mount info for global /dev instance */ extern struct vnodeops *devzvol_vnodeops; extern int sdev_vnodeops_tbl_size; extern const fs_operation_def_t sdev_vnodeops_tbl[]; extern const fs_operation_def_t devpts_vnodeops_tbl[]; extern const fs_operation_def_t devnet_vnodeops_tbl[]; extern const fs_operation_def_t devipnet_vnodeops_tbl[]; extern const fs_operation_def_t devvt_vnodeops_tbl[]; extern const fs_operation_def_t devsys_vnodeops_tbl[]; extern const fs_operation_def_t devpseudo_vnodeops_tbl[]; extern const fs_operation_def_t devzvol_vnodeops_tbl[]; extern sdev_nc_list_t *sdev_ncache; extern int sdev_reconfig_boot; extern int sdev_boot_state; extern int sdev_reconfig_verbose; extern int sdev_reconfig_disable; extern int sdev_nc_disable; extern int sdev_nc_disable_reset; extern int sdev_nc_verbose; extern taskq_t *sdev_taskq; /* * misc. defines */ #ifdef DEBUG extern int sdev_debug; #define SDEV_DEBUG 0x01 /* error messages to console/log */ #define SDEV_DEBUG_VOPS 0x02 /* vnode ops errors */ #define SDEV_DEBUG_DLF 0x04 /* trace devname_lookup_func */ #define SDEV_DEBUG_DRF 0x08 /* trace devname_readdir_func */ #define SDEV_DEBUG_NCACHE 0x10 /* negative cache tracing */ #define SDEV_DEBUG_DEVFSADMD 0x20 /* comm. of devnamefs & devfsadm */ #define SDEV_DEBUG_PTS 0x40 /* /dev/pts tracing */ #define SDEV_DEBUG_RECONFIG 0x80 /* events triggering reconfig */ #define SDEV_DEBUG_SDEV_NODE 0x100 /* trace sdev_node activities */ #define SDEV_DEBUG_PROFILE 0x200 /* trace sdev_profile */ #define SDEV_DEBUG_MODCTL 0x400 /* trace modctl activity */ #define SDEV_DEBUG_FLK 0x800 /* trace failed lookups */ #define SDEV_DEBUG_NET 0x1000 /* /dev/net tracing */ #define SDEV_DEBUG_ZVOL 0x2000 /* /dev/zvol/tracing */ #define sdcmn_err(args) if (sdev_debug & SDEV_DEBUG) printf args #define sdcmn_err2(args) if (sdev_debug & SDEV_DEBUG_VOPS) printf args #define sdcmn_err3(args) if (sdev_debug & SDEV_DEBUG_DLF) printf args #define sdcmn_err4(args) if (sdev_debug & SDEV_DEBUG_DRF) printf args #define sdcmn_err5(args) if (sdev_debug & SDEV_DEBUG_NCACHE) printf args #define sdcmn_err6(args) if (sdev_debug & SDEV_DEBUG_DEVFSADMD) printf args #define sdcmn_err7(args) if (sdev_debug & SDEV_DEBUG_PTS) printf args #define sdcmn_err8(args) if (sdev_debug & SDEV_DEBUG_RECONFIG) printf args #define sdcmn_err9(args) if (sdev_debug & SDEV_DEBUG_SDEV_NODE) printf args #define sdcmn_err10(args) if (sdev_debug & SDEV_DEBUG_PROFILE) printf args #define sdcmn_err11(args) if (sdev_debug & SDEV_DEBUG_MODCTL) printf args #define sdcmn_err12(args) if (sdev_debug & SDEV_DEBUG_NET) printf args #define sdcmn_err13(args) if (sdev_debug & SDEV_DEBUG_ZVOL) printf args #define impossible(args) printf args #else #define sdcmn_err(args) ((void)0) #define sdcmn_err2(args) ((void)0) #define sdcmn_err3(args) ((void)0) #define sdcmn_err4(args) ((void)0) #define sdcmn_err5(args) ((void)0) #define sdcmn_err6(args) ((void)0) #define sdcmn_err7(args) ((void)0) #define sdcmn_err8(args) ((void)0) #define sdcmn_err9(args) ((void)0) #define sdcmn_err10(args) ((void)0) #define sdcmn_err11(args) ((void)0) #define sdcmn_err12(args) ((void)0) #define sdcmn_err13(args) ((void)0) #define impossible(args) ((void)0) #endif #ifdef DEBUG #define SD_TRACE_FAILED_LOOKUP(ddv, nm, retried) \ if ((sdev_debug & SDEV_DEBUG_FLK) || \ ((retried) && (sdev_debug & SDEV_DEBUG_RECONFIG))) { \ printf("lookup of %s/%s by %s failed, line %d\n", \ (ddv)->sdev_name, (nm), curproc->p_user.u_comm, \ __LINE__); \ } #else #define SD_TRACE_FAILED_LOOKUP(ddv, nm, retried) ((void)0) #endif #endif /* _KERNEL */ #ifdef __cplusplus } #endif #endif /* _SYS_SDEV_IMPL_H */