Print this page
@@ -148,10 +148,16 @@
};
kmem_cache_t *sdev_node_cache; /* sdev_node cache */
int devtype; /* fstype */
+/* static */
+static struct vnodeops *sdev_get_vop(struct sdev_node *);
+static void sdev_set_no_negcache(struct sdev_node *);
+static fs_operation_def_t *sdev_merge_vtab(const fs_operation_def_t []);
+static void sdev_free_vtab(fs_operation_def_t *);
+
static void
sdev_prof_free(struct sdev_node *dv)
{
ASSERT(!SDEV_IS_GLOBAL(dv));
nvlist_free(dv->sdev_prof.dev_name);
@@ -305,11 +311,10 @@
len = strlen(ddv->sdev_path) + strlen(nm) + 2;
dv->sdev_path = kmem_alloc(len, KM_SLEEP);
(void) snprintf(dv->sdev_path, len, "%s/%s", ddv->sdev_path, nm);
/* overwritten for VLNK nodes */
dv->sdev_symlink = NULL;
- list_link_init(&dv->sdev_plist);
vp = SDEVTOV(dv);
vn_reinit(vp);
vp->v_vfsp = SDEVTOV(ddv)->v_vfsp;
if (vap)
@@ -394,11 +399,10 @@
dv->sdev_nlink = 1;
dv->sdev_symlink = i_ddi_strdup((char *)args, KM_SLEEP);
} else {
dv->sdev_nlink = 1;
}
- sdev_plugin_nodeready(dv);
if (!(SDEV_IS_GLOBAL(dv))) {
dv->sdev_origin = (struct sdev_node *)args;
dv->sdev_flags &= ~SDEV_PERSIST;
}
@@ -491,26 +495,41 @@
rw_exit(&dv->sdev_contents);
sdev_nc_node_exists(dv);
return (dv);
}
-struct sdev_vop_table vtab[] = {
- { "pts", devpts_vnodeops_tbl, &devpts_vnodeops, devpts_validate,
+/* directory dependent vop table */
+struct sdev_vop_table {
+ char *vt_name; /* subdirectory name */
+ const fs_operation_def_t *vt_service; /* vnodeops table */
+ struct vnodeops *vt_vops; /* constructed vop */
+ struct vnodeops **vt_global_vops; /* global container for vop */
+ int (*vt_vtor)(struct sdev_node *); /* validate sdev_node */
+ int vt_flags;
+};
+
+/*
+ * A nice improvement would be to provide a plug-in mechanism
+ * for this table instead of a const table.
+ */
+static struct sdev_vop_table vtab[] =
+{
+ { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate,
SDEV_DYNAMIC | SDEV_VTOR },
- { "vt", devvt_vnodeops_tbl, &devvt_vnodeops, devvt_validate,
+ { "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate,
SDEV_DYNAMIC | SDEV_VTOR },
- { "zvol", devzvol_vnodeops_tbl, &devzvol_vnodeops,
+ { "zvol", devzvol_vnodeops_tbl, NULL, &devzvol_vnodeops,
devzvol_validate, SDEV_ZONED | SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
- { "zcons", NULL, NULL, NULL, SDEV_NO_NCACHE },
+ { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE },
- { "net", devnet_vnodeops_tbl, &devnet_vnodeops, devnet_validate,
- SDEV_DYNAMIC | SDEV_VTOR | SDEV_SUBDIR },
+ { "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate,
+ SDEV_DYNAMIC | SDEV_VTOR },
- { "ipnet", devipnet_vnodeops_tbl, &devipnet_vnodeops,
+ { "ipnet", devipnet_vnodeops_tbl, NULL, &devipnet_vnodeops,
devipnet_validate, SDEV_DYNAMIC | SDEV_VTOR | SDEV_NO_NCACHE },
/*
* SDEV_DYNAMIC: prevent calling out to devfsadm, since only the
* lofi driver controls child nodes.
@@ -521,20 +540,138 @@
* In addition, devfsadm knows not to attempt a rmdir: a zone
* may hold a reference, which would zombify the node,
* preventing a mkdir.
*/
- { "lofi", NULL, NULL, NULL,
+ { "lofi", NULL, NULL, NULL, NULL,
SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
- { "rlofi", NULL, NULL, NULL,
+ { "rlofi", NULL, NULL, NULL, NULL,
SDEV_ZONED | SDEV_DYNAMIC | SDEV_PERSIST },
- { NULL, NULL, NULL, NULL, 0}
+ { NULL, NULL, NULL, NULL, NULL, 0}
};
+/*
+ * We need to match off of the sdev_path, not the sdev_name. We are only allowed
+ * to exist directly under /dev.
+ */
+struct sdev_vop_table *
+sdev_match(struct sdev_node *dv)
+{
+ int vlen;
+ int i;
+ const char *path;
+ if (strlen(dv->sdev_path) <= 5)
+ return (NULL);
+
+ if (strncmp(dv->sdev_path, "/dev/", 5) != 0)
+ return (NULL);
+ path = dv->sdev_path + 5;
+
+ for (i = 0; vtab[i].vt_name; i++) {
+ if (strcmp(vtab[i].vt_name, path) == 0)
+ return (&vtab[i]);
+ if (vtab[i].vt_flags & SDEV_SUBDIR) {
+ vlen = strlen(vtab[i].vt_name);
+ if ((strncmp(vtab[i].vt_name, path,
+ vlen - 1) == 0) && path[vlen] == '/')
+ return (&vtab[i]);
+ }
+
+ }
+ return (NULL);
+}
+
/*
+ * sets a directory's vnodeops if the directory is in the vtab;
+ */
+static struct vnodeops *
+sdev_get_vop(struct sdev_node *dv)
+{
+ struct sdev_vop_table *vtp;
+ char *path;
+
+ path = dv->sdev_path;
+ ASSERT(path);
+
+ /* gets the relative path to /dev/ */
+ path += 5;
+
+ /* gets the vtab entry it matches */
+ if ((vtp = sdev_match(dv)) != NULL) {
+ dv->sdev_flags |= vtp->vt_flags;
+ if (SDEV_IS_PERSIST(dv->sdev_dotdot) &&
+ (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv)))
+ dv->sdev_flags |= SDEV_PERSIST;
+
+ if (vtp->vt_vops) {
+ if (vtp->vt_global_vops)
+ *(vtp->vt_global_vops) = vtp->vt_vops;
+
+ return (vtp->vt_vops);
+ }
+
+ if (vtp->vt_service) {
+ fs_operation_def_t *templ;
+ templ = sdev_merge_vtab(vtp->vt_service);
+ if (vn_make_ops(vtp->vt_name,
+ (const fs_operation_def_t *)templ,
+ &vtp->vt_vops) != 0) {
+ cmn_err(CE_PANIC, "%s: malformed vnode ops\n",
+ vtp->vt_name);
+ /*NOTREACHED*/
+ }
+ if (vtp->vt_global_vops) {
+ *(vtp->vt_global_vops) = vtp->vt_vops;
+ }
+ sdev_free_vtab(templ);
+
+ return (vtp->vt_vops);
+ }
+
+ return (sdev_vnodeops);
+ }
+
+ /* child inherits the persistence of the parent */
+ if (SDEV_IS_PERSIST(dv->sdev_dotdot))
+ dv->sdev_flags |= SDEV_PERSIST;
+
+ return (sdev_vnodeops);
+}
+
+static void
+sdev_set_no_negcache(struct sdev_node *dv)
+{
+ int i;
+ char *path;
+
+ ASSERT(dv->sdev_path);
+ path = dv->sdev_path + strlen("/dev/");
+
+ for (i = 0; vtab[i].vt_name; i++) {
+ if (strcmp(vtab[i].vt_name, path) == 0) {
+ if (vtab[i].vt_flags & SDEV_NO_NCACHE)
+ dv->sdev_flags |= SDEV_NO_NCACHE;
+ break;
+ }
+ }
+}
+
+void *
+sdev_get_vtor(struct sdev_node *dv)
+{
+ struct sdev_vop_table *vtp;
+
+ vtp = sdev_match(dv);
+ if (vtp)
+ return ((void *)vtp->vt_vtor);
+ else
+ return (NULL);
+}
+
+/*
* Build the base root inode
*/
ino_t
sdev_mkino(struct sdev_node *dv)
{
@@ -808,15 +945,12 @@
if (dv->sdev_path) {
kmem_free(dv->sdev_path, strlen(dv->sdev_path) + 1);
dv->sdev_path = NULL;
}
- if (!SDEV_IS_GLOBAL(dv)) {
+ if (!SDEV_IS_GLOBAL(dv))
sdev_prof_free(dv);
- if (dv->sdev_vnode->v_type != VLNK && dv->sdev_origin != NULL)
- SDEV_RELE(dv->sdev_origin);
- }
if (SDEVTOV(dv)->v_type == VDIR) {
ASSERT(SDEV_FIRST_ENTRY(dv) == NULL);
avl_destroy(&dv->sdev_entries);
}
@@ -2809,10 +2943,50 @@
VN_RELE(vp);
return (error);
}
+extern int sdev_vnodeops_tbl_size;
+
+/*
+ * construct a new template with overrides from vtab
+ */
+static fs_operation_def_t *
+sdev_merge_vtab(const fs_operation_def_t tab[])
+{
+ fs_operation_def_t *new;
+ const fs_operation_def_t *tab_entry;
+
+ /* make a copy of standard vnode ops table */
+ new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
+ bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
+
+ /* replace the overrides from tab */
+ for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
+ fs_operation_def_t *std_entry = new;
+ while (std_entry->name) {
+ if (strcmp(tab_entry->name, std_entry->name) == 0) {
+ std_entry->func = tab_entry->func;
+ break;
+ }
+ std_entry++;
+ }
+ if (std_entry->name == NULL)
+ cmn_err(CE_NOTE, "sdev_merge_vtab: entry %s unused.",
+ tab_entry->name);
+ }
+
+ return (new);
+}
+
+/* free memory allocated by sdev_merge_vtab */
+static void
+sdev_free_vtab(fs_operation_def_t *new)
+{
+ kmem_free(new, sdev_vnodeops_tbl_size);
+}
+
/*
* a generic setattr() function
*
* note: flags only supports AT_UID and AT_GID.
* Future enhancements can be done for other types, e.g. AT_MODE