121864bc5SMatthew Dillon /*
2dd0e3cd7SMatthew Dillon * Copyright (c) 2009-2019 The DragonFly Project. All rights reserved.
321864bc5SMatthew Dillon *
421864bc5SMatthew Dillon * This code is derived from software contributed to The DragonFly Project
521864bc5SMatthew Dillon * by Alex Hornung <ahornung@gmail.com>
621864bc5SMatthew Dillon *
721864bc5SMatthew Dillon * Redistribution and use in source and binary forms, with or without
821864bc5SMatthew Dillon * modification, are permitted provided that the following conditions
921864bc5SMatthew Dillon * are met:
1021864bc5SMatthew Dillon *
1121864bc5SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
1221864bc5SMatthew Dillon * notice, this list of conditions and the following disclaimer.
1321864bc5SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
1421864bc5SMatthew Dillon * notice, this list of conditions and the following disclaimer in
1521864bc5SMatthew Dillon * the documentation and/or other materials provided with the
1621864bc5SMatthew Dillon * distribution.
1721864bc5SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
1821864bc5SMatthew Dillon * contributors may be used to endorse or promote products derived
1921864bc5SMatthew Dillon * from this software without specific, prior written permission.
2021864bc5SMatthew Dillon *
2121864bc5SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2221864bc5SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2321864bc5SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2421864bc5SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2521864bc5SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2621864bc5SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2721864bc5SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2821864bc5SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2921864bc5SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3021864bc5SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3121864bc5SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3221864bc5SMatthew Dillon * SUCH DAMAGE.
3321864bc5SMatthew Dillon */
3421864bc5SMatthew Dillon #include <sys/param.h>
3521864bc5SMatthew Dillon #include <sys/systm.h>
3621864bc5SMatthew Dillon #include <sys/kernel.h>
3762c0a8e4SMichael Neumann #include <sys/bus.h>
38805c8e8eSzrj #include <sys/malloc.h>
3921864bc5SMatthew Dillon #include <sys/mount.h>
4021864bc5SMatthew Dillon #include <sys/vnode.h>
4121864bc5SMatthew Dillon #include <sys/lock.h>
4291c397b9SJohannes Hofmann #include <sys/file.h>
4321864bc5SMatthew Dillon #include <sys/msgport.h>
4421864bc5SMatthew Dillon #include <sys/sysctl.h>
4521864bc5SMatthew Dillon #include <sys/ucred.h>
462c1e28ddSAlex Hornung #include <sys/devfs.h>
472c1e28ddSAlex Hornung #include <sys/devfs_rules.h>
483a3826b3SAlex Hornung #include <sys/udev.h>
4921864bc5SMatthew Dillon
50c9e9fb21SMatthew Dillon #include <sys/msgport2.h>
51c9e9fb21SMatthew Dillon #include <sys/spinlock2.h>
52c9e9fb21SMatthew Dillon #include <sys/sysref2.h>
53c9e9fb21SMatthew Dillon
5421864bc5SMatthew Dillon MALLOC_DEFINE(M_DEVFS, "devfs", "Device File System (devfs) allocations");
550cf7fc2cSSascha Wildner DEVFS_DEFINE_CLONE_BITMAP(ops_id);
5621864bc5SMatthew Dillon /*
5721864bc5SMatthew Dillon * SYSREF Integration - reference counting, allocation,
5821864bc5SMatthew Dillon * sysid and syslink integration.
5921864bc5SMatthew Dillon */
6021864bc5SMatthew Dillon static void devfs_cdev_terminate(cdev_t dev);
61e654922cSMatthew Dillon static void devfs_cdev_lock(cdev_t dev);
62e654922cSMatthew Dillon static void devfs_cdev_unlock(cdev_t dev);
6321864bc5SMatthew Dillon static struct sysref_class cdev_sysref_class = {
6421864bc5SMatthew Dillon .name = "cdev",
6521864bc5SMatthew Dillon .mtype = M_DEVFS,
6621864bc5SMatthew Dillon .proto = SYSREF_PROTO_DEV,
6721864bc5SMatthew Dillon .offset = offsetof(struct cdev, si_sysref),
6821864bc5SMatthew Dillon .objsize = sizeof(struct cdev),
69521f81c7SMatthew Dillon .nom_cache = 32,
7021864bc5SMatthew Dillon .flags = 0,
7121864bc5SMatthew Dillon .ops = {
72e654922cSMatthew Dillon .terminate = (sysref_terminate_func_t)devfs_cdev_terminate,
73e654922cSMatthew Dillon .lock = (sysref_lock_func_t)devfs_cdev_lock,
74e654922cSMatthew Dillon .unlock = (sysref_unlock_func_t)devfs_cdev_unlock
7521864bc5SMatthew Dillon }
7621864bc5SMatthew Dillon };
7721864bc5SMatthew Dillon
7821864bc5SMatthew Dillon static struct objcache *devfs_node_cache;
7921864bc5SMatthew Dillon static struct objcache *devfs_msg_cache;
8021864bc5SMatthew Dillon static struct objcache *devfs_dev_cache;
8121864bc5SMatthew Dillon
8221864bc5SMatthew Dillon static struct objcache_malloc_args devfs_node_malloc_args = {
8321864bc5SMatthew Dillon sizeof(struct devfs_node), M_DEVFS };
8421864bc5SMatthew Dillon struct objcache_malloc_args devfs_msg_malloc_args = {
8521864bc5SMatthew Dillon sizeof(struct devfs_msg), M_DEVFS };
8621864bc5SMatthew Dillon struct objcache_malloc_args devfs_dev_malloc_args = {
8721864bc5SMatthew Dillon sizeof(struct cdev), M_DEVFS };
8821864bc5SMatthew Dillon
89bc185c5aSAlex Hornung static struct devfs_dev_head devfs_dev_list =
90bc185c5aSAlex Hornung TAILQ_HEAD_INITIALIZER(devfs_dev_list);
91bc185c5aSAlex Hornung static struct devfs_mnt_head devfs_mnt_list =
92bc185c5aSAlex Hornung TAILQ_HEAD_INITIALIZER(devfs_mnt_list);
93bc185c5aSAlex Hornung static struct devfs_chandler_head devfs_chandler_list =
94bc185c5aSAlex Hornung TAILQ_HEAD_INITIALIZER(devfs_chandler_list);
95bc185c5aSAlex Hornung static struct devfs_alias_head devfs_alias_list =
96bc185c5aSAlex Hornung TAILQ_HEAD_INITIALIZER(devfs_alias_list);
97176de024SAlex Hornung static struct devfs_dev_ops_head devfs_dev_ops_list =
98176de024SAlex Hornung TAILQ_HEAD_INITIALIZER(devfs_dev_ops_list);
9921864bc5SMatthew Dillon
10021864bc5SMatthew Dillon struct lock devfs_lock;
101c0739b3cSMatthew Dillon struct lwkt_token devfs_token;
10221864bc5SMatthew Dillon static struct lwkt_port devfs_dispose_port;
10321864bc5SMatthew Dillon static struct lwkt_port devfs_msg_port;
10421864bc5SMatthew Dillon static struct thread *td_core;
10521864bc5SMatthew Dillon
10621864bc5SMatthew Dillon static struct spinlock ino_lock;
107d0fe8596SMatthew Dillon static ino_t d_ino;
108d0fe8596SMatthew Dillon static int devfs_debug_enable;
109d0fe8596SMatthew Dillon static int devfs_run;
11021864bc5SMatthew Dillon
11121864bc5SMatthew Dillon static ino_t devfs_fetch_ino(void);
112dd0e3cd7SMatthew Dillon static int devfs_reference_ops(struct dev_ops *ops);
113dd0e3cd7SMatthew Dillon static void devfs_release_ops(struct dev_ops *ops);
11421864bc5SMatthew Dillon static int devfs_create_all_dev_worker(struct devfs_node *);
11521864bc5SMatthew Dillon static int devfs_create_dev_worker(cdev_t, uid_t, gid_t, int);
11621864bc5SMatthew Dillon static int devfs_destroy_dev_worker(cdev_t);
11772ea429eSAlex Hornung static int devfs_destroy_related_worker(cdev_t);
11821864bc5SMatthew Dillon static int devfs_destroy_dev_by_ops_worker(struct dev_ops *, int);
11921864bc5SMatthew Dillon static int devfs_propagate_dev(cdev_t, int);
120ca8d7677SMatthew Dillon static int devfs_unlink_dev(cdev_t dev);
121d0fe8596SMatthew Dillon static void devfs_msg_exec(devfs_msg_t msg);
12221864bc5SMatthew Dillon
12307dfa375SAlex Hornung static int devfs_chandler_add_worker(const char *, d_clone_t *);
12407dfa375SAlex Hornung static int devfs_chandler_del_worker(const char *);
12521864bc5SMatthew Dillon
12621864bc5SMatthew Dillon static void devfs_msg_autofree_reply(lwkt_port_t, lwkt_msg_t);
12721864bc5SMatthew Dillon static void devfs_msg_core(void *);
12821864bc5SMatthew Dillon
12921864bc5SMatthew Dillon static int devfs_find_device_by_name_worker(devfs_msg_t);
1302ac7d105SSascha Wildner static int devfs_find_device_by_devid_worker(devfs_msg_t);
13121864bc5SMatthew Dillon
13221864bc5SMatthew Dillon static int devfs_apply_reset_rules_caller(char *, int);
13321864bc5SMatthew Dillon
1343a3826b3SAlex Hornung static int devfs_scan_callback_worker(devfs_scan_t *, void *);
13521864bc5SMatthew Dillon
136bc185c5aSAlex Hornung static struct devfs_node *devfs_resolve_or_create_dir(struct devfs_node *,
137bc185c5aSAlex Hornung char *, size_t, int);
13821864bc5SMatthew Dillon
13921864bc5SMatthew Dillon static int devfs_make_alias_worker(struct devfs_alias *);
1408312ca30SAlex Hornung static int devfs_destroy_alias_worker(struct devfs_alias *);
14121864bc5SMatthew Dillon static int devfs_alias_remove(cdev_t);
14221864bc5SMatthew Dillon static int devfs_alias_reap(void);
1438312ca30SAlex Hornung static int devfs_alias_propagate(struct devfs_alias *, int);
14421864bc5SMatthew Dillon static int devfs_alias_apply(struct devfs_node *, struct devfs_alias *);
14521864bc5SMatthew Dillon static int devfs_alias_check_create(struct devfs_node *);
14621864bc5SMatthew Dillon
14772ea429eSAlex Hornung static int devfs_clr_related_flag_worker(cdev_t, uint32_t);
14872ea429eSAlex Hornung static int devfs_destroy_related_without_flag_worker(cdev_t, uint32_t);
149ca8d7677SMatthew Dillon
15066abefa5SAlex Hornung static void *devfs_reaperp_callback(struct devfs_node *, void *);
1511bef7707SMatthew Dillon static void devfs_iterate_orphans_unmount(struct mount *mp);
15266abefa5SAlex Hornung static void *devfs_gc_dirs_callback(struct devfs_node *, void *);
15366abefa5SAlex Hornung static void *devfs_gc_links_callback(struct devfs_node *, struct devfs_node *);
15466abefa5SAlex Hornung static void *
15566abefa5SAlex Hornung devfs_inode_to_vnode_worker_callback(struct devfs_node *, ino_t *);
15666abefa5SAlex Hornung
15721864bc5SMatthew Dillon /*
158bc185c5aSAlex Hornung * devfs_debug() is a SYSCTL and TUNABLE controlled debug output function
159bc185c5aSAlex Hornung * using kvprintf
16021864bc5SMatthew Dillon */
16121864bc5SMatthew Dillon int
devfs_debug(int level,char * fmt,...)16221864bc5SMatthew Dillon devfs_debug(int level, char *fmt, ...)
16321864bc5SMatthew Dillon {
16421864bc5SMatthew Dillon __va_list ap;
16521864bc5SMatthew Dillon
16621864bc5SMatthew Dillon __va_start(ap, fmt);
16721864bc5SMatthew Dillon if (level <= devfs_debug_enable)
16821864bc5SMatthew Dillon kvprintf(fmt, ap);
16921864bc5SMatthew Dillon __va_end(ap);
17021864bc5SMatthew Dillon
17121864bc5SMatthew Dillon return 0;
17221864bc5SMatthew Dillon }
17321864bc5SMatthew Dillon
17421864bc5SMatthew Dillon /*
175ca8d7677SMatthew Dillon * devfs_allocp() Allocates a new devfs node with the specified
176ca8d7677SMatthew Dillon * parameters. The node is also automatically linked into the topology
177ca8d7677SMatthew Dillon * if a parent is specified. It also calls the rule and alias stuff to
178ca8d7677SMatthew Dillon * be applied on the new node
17921864bc5SMatthew Dillon */
18021864bc5SMatthew Dillon struct devfs_node *
devfs_allocp(devfs_nodetype devfsnodetype,char * name,struct devfs_node * parent,struct mount * mp,cdev_t dev)181ca8d7677SMatthew Dillon devfs_allocp(devfs_nodetype devfsnodetype, char *name,
182ca8d7677SMatthew Dillon struct devfs_node *parent, struct mount *mp, cdev_t dev)
18321864bc5SMatthew Dillon {
18421864bc5SMatthew Dillon struct devfs_node *node = NULL;
18521864bc5SMatthew Dillon size_t namlen = strlen(name);
18621864bc5SMatthew Dillon
18721864bc5SMatthew Dillon node = objcache_get(devfs_node_cache, M_WAITOK);
188ca8d7677SMatthew Dillon bzero(node, sizeof(*node));
189ca8d7677SMatthew Dillon
190d4f19b8bSMatthew Dillon atomic_add_long(&DEVFS_MNTDATA(mp)->leak_count, 1);
19121864bc5SMatthew Dillon
192ca8d7677SMatthew Dillon node->d_dev = NULL;
19321864bc5SMatthew Dillon node->nchildren = 1;
19421864bc5SMatthew Dillon node->mp = mp;
19521864bc5SMatthew Dillon node->d_dir.d_ino = devfs_fetch_ino();
19621864bc5SMatthew Dillon
197bc185c5aSAlex Hornung /*
198bc185c5aSAlex Hornung * Cookie jar for children. Leave 0 and 1 for '.' and '..' entries
199bc185c5aSAlex Hornung * respectively.
200bc185c5aSAlex Hornung */
201bc185c5aSAlex Hornung node->cookie_jar = 2;
202bc185c5aSAlex Hornung
203bc185c5aSAlex Hornung /*
204bc185c5aSAlex Hornung * Access Control members
205bc185c5aSAlex Hornung */
206bc185c5aSAlex Hornung node->mode = DEVFS_DEFAULT_MODE;
207bc185c5aSAlex Hornung node->uid = DEVFS_DEFAULT_UID;
208bc185c5aSAlex Hornung node->gid = DEVFS_DEFAULT_GID;
20921864bc5SMatthew Dillon
21021864bc5SMatthew Dillon switch (devfsnodetype) {
2118e78a293SSascha Wildner case Nroot:
212bc185c5aSAlex Hornung /*
213bc185c5aSAlex Hornung * Ensure that we don't recycle the root vnode by marking it as
214bc185c5aSAlex Hornung * linked into the topology.
215bc185c5aSAlex Hornung */
216894bbb25SAlex Hornung node->flags |= DEVFS_NODE_LINKED;
2178e78a293SSascha Wildner case Ndir:
21821864bc5SMatthew Dillon TAILQ_INIT(DEVFS_DENODE_HEAD(node));
21921864bc5SMatthew Dillon node->d_dir.d_type = DT_DIR;
22021864bc5SMatthew Dillon node->nchildren = 2;
22121864bc5SMatthew Dillon break;
22221864bc5SMatthew Dillon
2238e78a293SSascha Wildner case Nlink:
22421864bc5SMatthew Dillon node->d_dir.d_type = DT_LNK;
22521864bc5SMatthew Dillon break;
22621864bc5SMatthew Dillon
2278e78a293SSascha Wildner case Nreg:
22821864bc5SMatthew Dillon node->d_dir.d_type = DT_REG;
22921864bc5SMatthew Dillon break;
23021864bc5SMatthew Dillon
2318e78a293SSascha Wildner case Ndev:
23221864bc5SMatthew Dillon if (dev != NULL) {
23321864bc5SMatthew Dillon node->d_dir.d_type = DT_CHR;
23421864bc5SMatthew Dillon node->d_dev = dev;
23521864bc5SMatthew Dillon
236bc185c5aSAlex Hornung node->mode = dev->si_perms;
237bc185c5aSAlex Hornung node->uid = dev->si_uid;
238bc185c5aSAlex Hornung node->gid = dev->si_gid;
23921864bc5SMatthew Dillon
24021864bc5SMatthew Dillon devfs_alias_check_create(node);
24121864bc5SMatthew Dillon }
24221864bc5SMatthew Dillon break;
24321864bc5SMatthew Dillon
24421864bc5SMatthew Dillon default:
24521864bc5SMatthew Dillon panic("devfs_allocp: unknown node type");
24621864bc5SMatthew Dillon }
24721864bc5SMatthew Dillon
24821864bc5SMatthew Dillon node->v_node = NULL;
24921864bc5SMatthew Dillon node->node_type = devfsnodetype;
25021864bc5SMatthew Dillon
251bc185c5aSAlex Hornung /* Initialize the dirent structure of each devfs vnode */
25221864bc5SMatthew Dillon node->d_dir.d_namlen = namlen;
253ca8d7677SMatthew Dillon node->d_dir.d_name = kmalloc(namlen+1, M_DEVFS, M_WAITOK);
25421864bc5SMatthew Dillon memcpy(node->d_dir.d_name, name, namlen);
25521864bc5SMatthew Dillon node->d_dir.d_name[namlen] = '\0';
25621864bc5SMatthew Dillon
25721864bc5SMatthew Dillon /* Initialize the parent node element */
25821864bc5SMatthew Dillon node->parent = parent;
25921864bc5SMatthew Dillon
260bc185c5aSAlex Hornung /* Initialize *time members */
261d489a79aSMatthew Dillon vfs_timestamp(&node->atime);
26221864bc5SMatthew Dillon node->mtime = node->ctime = node->atime;
26321864bc5SMatthew Dillon
264ca8d7677SMatthew Dillon /*
265ca8d7677SMatthew Dillon * Associate with parent as last step, clean out namecache
266ca8d7677SMatthew Dillon * reference.
267ca8d7677SMatthew Dillon */
2681bef7707SMatthew Dillon if (parent) {
2691bef7707SMatthew Dillon if (parent->node_type == Nroot ||
2701bef7707SMatthew Dillon parent->node_type == Ndir) {
271ca8d7677SMatthew Dillon parent->nchildren++;
272ca8d7677SMatthew Dillon node->cookie = parent->cookie_jar++;
273ca8d7677SMatthew Dillon node->flags |= DEVFS_NODE_LINKED;
274ca8d7677SMatthew Dillon TAILQ_INSERT_TAIL(DEVFS_DENODE_HEAD(parent), node, link);
275ca8d7677SMatthew Dillon
276ca8d7677SMatthew Dillon /* This forces negative namecache lookups to clear */
277ca8d7677SMatthew Dillon ++mp->mnt_namecache_gen;
2781bef7707SMatthew Dillon } else {
2791bef7707SMatthew Dillon kprintf("devfs: Cannot link node %p (%s) "
2801bef7707SMatthew Dillon "into %p (%s)\n",
2811bef7707SMatthew Dillon node, node->d_dir.d_name,
2821bef7707SMatthew Dillon parent, parent->d_dir.d_name);
2831bef7707SMatthew Dillon print_backtrace(-1);
2841bef7707SMatthew Dillon }
285ca8d7677SMatthew Dillon }
28621864bc5SMatthew Dillon
2876870c3d4SMatthew Dillon /*
2881bef7707SMatthew Dillon * Apply rules (requires root node, skip if we are creating the root
2891bef7707SMatthew Dillon * node)
2906870c3d4SMatthew Dillon */
2916870c3d4SMatthew Dillon if (DEVFS_MNTDATA(mp)->root_node)
292951cde04SAlex Hornung devfs_rule_check_apply(node, NULL);
293951cde04SAlex Hornung
294d4f19b8bSMatthew Dillon atomic_add_long(&DEVFS_MNTDATA(mp)->file_count, 1);
295aee6fa68SAlex Hornung
29621864bc5SMatthew Dillon return node;
29721864bc5SMatthew Dillon }
29821864bc5SMatthew Dillon
29921864bc5SMatthew Dillon /*
30021864bc5SMatthew Dillon * devfs_allocv() allocates a new vnode based on a devfs node.
30121864bc5SMatthew Dillon */
30221864bc5SMatthew Dillon int
devfs_allocv(struct vnode ** vpp,struct devfs_node * node)30321864bc5SMatthew Dillon devfs_allocv(struct vnode **vpp, struct devfs_node *node)
30421864bc5SMatthew Dillon {
30521864bc5SMatthew Dillon struct vnode *vp;
30621864bc5SMatthew Dillon int error = 0;
30721864bc5SMatthew Dillon
30821864bc5SMatthew Dillon KKASSERT(node);
30921864bc5SMatthew Dillon
3100180f2fdSMatthew Dillon /*
3110180f2fdSMatthew Dillon * devfs master lock must not be held across a vget() call, we have
3120180f2fdSMatthew Dillon * to hold our ad-hoc vp to avoid a free race from destroying the
3130180f2fdSMatthew Dillon * contents of the structure. The vget() will interlock recycles
3140180f2fdSMatthew Dillon * for us.
3150180f2fdSMatthew Dillon */
31621864bc5SMatthew Dillon try_again:
31721864bc5SMatthew Dillon while ((vp = node->v_node) != NULL) {
3180180f2fdSMatthew Dillon vhold(vp);
3190180f2fdSMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
32021864bc5SMatthew Dillon error = vget(vp, LK_EXCLUSIVE);
3210180f2fdSMatthew Dillon vdrop(vp);
3220180f2fdSMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
323495d3a1eSMatthew Dillon if (error == 0) {
32421864bc5SMatthew Dillon *vpp = vp;
32521864bc5SMatthew Dillon goto out;
32621864bc5SMatthew Dillon }
327495d3a1eSMatthew Dillon if (error != ENOENT) {
328495d3a1eSMatthew Dillon *vpp = NULL;
329495d3a1eSMatthew Dillon goto out;
330495d3a1eSMatthew Dillon }
33121864bc5SMatthew Dillon }
33221864bc5SMatthew Dillon
3330180f2fdSMatthew Dillon /*
3340180f2fdSMatthew Dillon * devfs master lock must not be held across a getnewvnode() call.
3350180f2fdSMatthew Dillon */
3360180f2fdSMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
3370180f2fdSMatthew Dillon if ((error = getnewvnode(VT_DEVFS, node->mp, vpp, 0, 0)) != 0) {
3380180f2fdSMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
33921864bc5SMatthew Dillon goto out;
3400180f2fdSMatthew Dillon }
3410180f2fdSMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
34221864bc5SMatthew Dillon
34321864bc5SMatthew Dillon vp = *vpp;
34421864bc5SMatthew Dillon
34521864bc5SMatthew Dillon if (node->v_node != NULL) {
34621864bc5SMatthew Dillon vp->v_type = VBAD;
34721864bc5SMatthew Dillon vx_put(vp);
34821864bc5SMatthew Dillon goto try_again;
34921864bc5SMatthew Dillon }
35021864bc5SMatthew Dillon
35121864bc5SMatthew Dillon vp->v_data = node;
35221864bc5SMatthew Dillon node->v_node = vp;
35321864bc5SMatthew Dillon
35421864bc5SMatthew Dillon switch (node->node_type) {
3558e78a293SSascha Wildner case Nroot:
3562247fe02SMatthew Dillon vsetflags(vp, VROOT);
3572247fe02SMatthew Dillon /* fall through */
3588e78a293SSascha Wildner case Ndir:
35921864bc5SMatthew Dillon vp->v_type = VDIR;
36021864bc5SMatthew Dillon break;
36121864bc5SMatthew Dillon
3628e78a293SSascha Wildner case Nlink:
36321864bc5SMatthew Dillon vp->v_type = VLNK;
36421864bc5SMatthew Dillon break;
36521864bc5SMatthew Dillon
3668e78a293SSascha Wildner case Nreg:
36721864bc5SMatthew Dillon vp->v_type = VREG;
36821864bc5SMatthew Dillon break;
36921864bc5SMatthew Dillon
3708e78a293SSascha Wildner case Ndev:
37121864bc5SMatthew Dillon vp->v_type = VCHR;
37221864bc5SMatthew Dillon KKASSERT(node->d_dev);
37321864bc5SMatthew Dillon
37421864bc5SMatthew Dillon vp->v_uminor = node->d_dev->si_uminor;
375619e8f47SSascha Wildner vp->v_umajor = node->d_dev->si_umajor;
376bc185c5aSAlex Hornung
3779b823501SAlex Hornung v_associate_rdev(vp, node->d_dev);
37821864bc5SMatthew Dillon vp->v_ops = &node->mp->mnt_vn_spec_ops;
379697e11b7SMatthew Dillon if (node->d_dev->si_ops->head.flags & D_KVABIO)
380697e11b7SMatthew Dillon vsetflags(vp, VKVABIO);
38121864bc5SMatthew Dillon break;
38221864bc5SMatthew Dillon
38321864bc5SMatthew Dillon default:
38421864bc5SMatthew Dillon panic("devfs_allocv: unknown node type");
38521864bc5SMatthew Dillon }
386fc36a10bSMatthew Dillon vx_downgrade(vp); /* downgrade VX lock to VN lock */
38721864bc5SMatthew Dillon
38821864bc5SMatthew Dillon out:
38921864bc5SMatthew Dillon return error;
39021864bc5SMatthew Dillon }
39121864bc5SMatthew Dillon
39221864bc5SMatthew Dillon /*
39321864bc5SMatthew Dillon * devfs_allocvp allocates both a devfs node (with the given settings) and a vnode
39421864bc5SMatthew Dillon * based on the newly created devfs node.
39521864bc5SMatthew Dillon */
39621864bc5SMatthew Dillon int
devfs_allocvp(struct mount * mp,struct vnode ** vpp,devfs_nodetype devfsnodetype,char * name,struct devfs_node * parent,cdev_t dev)39721864bc5SMatthew Dillon devfs_allocvp(struct mount *mp, struct vnode **vpp, devfs_nodetype devfsnodetype,
39821864bc5SMatthew Dillon char *name, struct devfs_node *parent, cdev_t dev)
39921864bc5SMatthew Dillon {
40021864bc5SMatthew Dillon struct devfs_node *node;
40121864bc5SMatthew Dillon
40221864bc5SMatthew Dillon node = devfs_allocp(devfsnodetype, name, parent, mp, dev);
403bc185c5aSAlex Hornung
40421864bc5SMatthew Dillon if (node != NULL)
40521864bc5SMatthew Dillon devfs_allocv(vpp, node);
40621864bc5SMatthew Dillon else
40721864bc5SMatthew Dillon *vpp = NULL;
40821864bc5SMatthew Dillon
40921864bc5SMatthew Dillon return 0;
41021864bc5SMatthew Dillon }
41121864bc5SMatthew Dillon
41221864bc5SMatthew Dillon /*
413ca8d7677SMatthew Dillon * Destroy the devfs_node. The node must be unlinked from the topology.
414ca8d7677SMatthew Dillon *
415ca8d7677SMatthew Dillon * This function will also destroy any vnode association with the node
416ca8d7677SMatthew Dillon * and device.
417ca8d7677SMatthew Dillon *
418ca8d7677SMatthew Dillon * The cdev_t itself remains intact.
419d4f19b8bSMatthew Dillon *
420d4f19b8bSMatthew Dillon * The core lock is not necessarily held on call and must be temporarily
421d4f19b8bSMatthew Dillon * released if it is to avoid a deadlock.
42221864bc5SMatthew Dillon */
42362e9e417SMatthew Dillon void
devfs_freep(struct devfs_node * node)42421864bc5SMatthew Dillon devfs_freep(struct devfs_node *node)
42521864bc5SMatthew Dillon {
426ca8d7677SMatthew Dillon struct vnode *vp;
427731fd4ccSMatthew Dillon int maxloops;
428ca8d7677SMatthew Dillon
42921864bc5SMatthew Dillon KKASSERT(node);
43021864bc5SMatthew Dillon
431a4141af4SMatthew Dillon /*
43262e9e417SMatthew Dillon * It is possible for devfs_freep() to race a destruction due
43362e9e417SMatthew Dillon * to having to release the lock below. We use DEVFS_DESTROYED
434770f8279SMatthew Dillon * to interlock the race (mediated by devfs_lock)
435770f8279SMatthew Dillon *
436770f8279SMatthew Dillon * We use NLINKSWAIT to indicate that the node couldn't be
437770f8279SMatthew Dillon * freed due to having pending nlinks. We can free
438770f8279SMatthew Dillon * the node when nlinks drops to 0. This should never print
439770f8279SMatthew Dillon * a "(null)" name, if it ever does there are still unresolved
440770f8279SMatthew Dillon * issues.
441a4141af4SMatthew Dillon */
44262e9e417SMatthew Dillon if (node->flags & DEVFS_DESTROYED) {
443770f8279SMatthew Dillon if ((node->flags & DEVFS_NLINKSWAIT) &&
444770f8279SMatthew Dillon node->nlinks == 0) {
445770f8279SMatthew Dillon kprintf("devfs: final node '%s' on nlinks\n",
446770f8279SMatthew Dillon node->d_dir.d_name);
447770f8279SMatthew Dillon if (node->d_dir.d_name) {
448770f8279SMatthew Dillon kfree(node->d_dir.d_name, M_DEVFS);
449770f8279SMatthew Dillon node->d_dir.d_name = NULL;
450770f8279SMatthew Dillon }
451770f8279SMatthew Dillon objcache_put(devfs_node_cache, node);
452770f8279SMatthew Dillon } else {
453731fd4ccSMatthew Dillon kprintf("devfs: race avoided node '%s' (%p)\n",
454731fd4ccSMatthew Dillon node->d_dir.d_name, node);
455770f8279SMatthew Dillon }
45662e9e417SMatthew Dillon return;
45762e9e417SMatthew Dillon }
458a4141af4SMatthew Dillon node->flags |= DEVFS_DESTROYED;
459a4141af4SMatthew Dillon
460a4141af4SMatthew Dillon /*
46162e9e417SMatthew Dillon * Items we have to dispose of before potentially releasing
46262e9e417SMatthew Dillon * devfs_lock.
46362e9e417SMatthew Dillon *
46462e9e417SMatthew Dillon * Remove the node from the orphan list if it is still on it.
46562e9e417SMatthew Dillon */
46662e9e417SMatthew Dillon atomic_subtract_long(&DEVFS_MNTDATA(node->mp)->leak_count, 1);
467770f8279SMatthew Dillon atomic_subtract_long(&DEVFS_MNTDATA(node->mp)->file_count, 1);
46862e9e417SMatthew Dillon if (node->flags & DEVFS_ORPHANED)
46962e9e417SMatthew Dillon devfs_tracer_del_orphan(node);
47062e9e417SMatthew Dillon
47162e9e417SMatthew Dillon /*
472770f8279SMatthew Dillon * At this point only the vp points to node, and node cannot be
473770f8279SMatthew Dillon * physically freed because we own DEVFS_DESTROYED.
474770f8279SMatthew Dillon *
475770f8279SMatthew Dillon * We must dispose of the vnode without deadlocking or racing
476770f8279SMatthew Dillon * against e.g. a vnode reclaim.
477a4141af4SMatthew Dillon *
478a4141af4SMatthew Dillon * This also prevents the vnode reclaim code from double-freeing
479a4141af4SMatthew Dillon * the node. The vget() is required to safely modified the vp
480a4141af4SMatthew Dillon * and cycle the refs to terminate an inactive vp.
481a4141af4SMatthew Dillon */
482731fd4ccSMatthew Dillon maxloops = 1000;
483770f8279SMatthew Dillon while ((vp = node->v_node) != NULL) {
484770f8279SMatthew Dillon int relock;
485770f8279SMatthew Dillon
486731fd4ccSMatthew Dillon vhold(vp);
487d4f19b8bSMatthew Dillon if (lockstatus(&devfs_lock, curthread) == LK_EXCLUSIVE) {
488a4141af4SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
489d4f19b8bSMatthew Dillon relock = 1;
490d4f19b8bSMatthew Dillon } else {
491d4f19b8bSMatthew Dillon relock = 0;
492d4f19b8bSMatthew Dillon }
493770f8279SMatthew Dillon if (node->v_node == NULL) {
494770f8279SMatthew Dillon /* reclaim race, mediated by devfs_lock */
495731fd4ccSMatthew Dillon vdrop(vp);
496770f8279SMatthew Dillon } else if (vget(vp, LK_EXCLUSIVE | LK_RETRY) == 0) {
497731fd4ccSMatthew Dillon vdrop(vp);
498a4141af4SMatthew Dillon v_release_rdev(vp);
499a4141af4SMatthew Dillon vp->v_data = NULL;
500a4141af4SMatthew Dillon node->v_node = NULL;
501a4141af4SMatthew Dillon vput(vp);
502770f8279SMatthew Dillon } else {
503770f8279SMatthew Dillon /* reclaim race, mediated by devfs_lock */
504731fd4ccSMatthew Dillon vdrop(vp);
505a4141af4SMatthew Dillon }
50662e9e417SMatthew Dillon if (relock)
50762e9e417SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
508731fd4ccSMatthew Dillon if (--maxloops == 0) {
509731fd4ccSMatthew Dillon kprintf("devfs_freep: livelock on node %p\n", node);
510731fd4ccSMatthew Dillon break;
511731fd4ccSMatthew Dillon }
512770f8279SMatthew Dillon }
51362e9e417SMatthew Dillon
514a4141af4SMatthew Dillon /*
515a4141af4SMatthew Dillon * Remaining cleanup
516a4141af4SMatthew Dillon */
51721864bc5SMatthew Dillon if (node->symlink_name) {
51821864bc5SMatthew Dillon kfree(node->symlink_name, M_DEVFS);
51921864bc5SMatthew Dillon node->symlink_name = NULL;
52021864bc5SMatthew Dillon }
521770f8279SMatthew Dillon
522770f8279SMatthew Dillon /*
523770f8279SMatthew Dillon * We cannot actually free the node if it still has
524770f8279SMatthew Dillon * nlinks.
525770f8279SMatthew Dillon */
526770f8279SMatthew Dillon if (node->nlinks) {
527770f8279SMatthew Dillon node->flags |= DEVFS_NLINKSWAIT;
528770f8279SMatthew Dillon } else {
5294062d050SMatthew Dillon if (node->d_dir.d_name) {
530ca8d7677SMatthew Dillon kfree(node->d_dir.d_name, M_DEVFS);
5314062d050SMatthew Dillon node->d_dir.d_name = NULL;
5324062d050SMatthew Dillon }
53321864bc5SMatthew Dillon objcache_put(devfs_node_cache, node);
53421864bc5SMatthew Dillon }
535770f8279SMatthew Dillon }
53621864bc5SMatthew Dillon
53721864bc5SMatthew Dillon /*
53840c007d7SFrançois Tigeot * Returns a valid vp associated with the devfs alias node or NULL
53940c007d7SFrançois Tigeot */
devfs_alias_getvp(struct devfs_node * node)54040c007d7SFrançois Tigeot static void *devfs_alias_getvp(struct devfs_node *node)
54140c007d7SFrançois Tigeot {
542e2e3dd73SFrançois Tigeot struct devfs_node *found = node;
543e2e3dd73SFrançois Tigeot int depth = 0;
54440c007d7SFrançois Tigeot
545e2e3dd73SFrançois Tigeot while ((found->node_type == Nlink) && (found->link_target)) {
546e2e3dd73SFrançois Tigeot if (depth >= 8) {
547e2e3dd73SFrançois Tigeot devfs_debug(DEVFS_DEBUG_SHOW, "Recursive link or depth >= 8");
548e2e3dd73SFrançois Tigeot break;
549e2e3dd73SFrançois Tigeot }
55040c007d7SFrançois Tigeot
551e2e3dd73SFrançois Tigeot found = found->link_target;
552e2e3dd73SFrançois Tigeot ++depth;
553e2e3dd73SFrançois Tigeot }
55440c007d7SFrançois Tigeot
555e2e3dd73SFrançois Tigeot return found->v_node;
55640c007d7SFrançois Tigeot }
55740c007d7SFrançois Tigeot
55840c007d7SFrançois Tigeot /*
559ca8d7677SMatthew Dillon * Unlink the devfs node from the topology and add it to the orphan list.
560ca8d7677SMatthew Dillon * The node will later be destroyed by freep.
561ca8d7677SMatthew Dillon *
562ca8d7677SMatthew Dillon * Any vnode association, including the v_rdev and v_data, remains intact
563ca8d7677SMatthew Dillon * until the freep.
56421864bc5SMatthew Dillon */
56562e9e417SMatthew Dillon void
devfs_unlinkp(struct devfs_node * node)56621864bc5SMatthew Dillon devfs_unlinkp(struct devfs_node *node)
56721864bc5SMatthew Dillon {
56821864bc5SMatthew Dillon struct devfs_node *parent;
5691bef7707SMatthew Dillon struct devfs_node *target;
5701bef7707SMatthew Dillon struct vnode *vp;
57121864bc5SMatthew Dillon KKASSERT(node);
57221864bc5SMatthew Dillon
573bc185c5aSAlex Hornung /*
574bc185c5aSAlex Hornung * Add the node to the orphan list, so it is referenced somewhere, to
575bc185c5aSAlex Hornung * so we don't leak it.
576bc185c5aSAlex Hornung */
57721864bc5SMatthew Dillon devfs_tracer_add_orphan(node);
578bc185c5aSAlex Hornung
57921864bc5SMatthew Dillon parent = node->parent;
5801bef7707SMatthew Dillon node->parent = NULL;
58121864bc5SMatthew Dillon
582ca8d7677SMatthew Dillon /*
583ca8d7677SMatthew Dillon * If the parent is known we can unlink the node out of the topology
584ca8d7677SMatthew Dillon */
5851bef7707SMatthew Dillon if (node->flags & DEVFS_NODE_LINKED) {
58621864bc5SMatthew Dillon if (parent) {
58721864bc5SMatthew Dillon TAILQ_REMOVE(DEVFS_DENODE_HEAD(parent), node, link);
58821864bc5SMatthew Dillon parent->nchildren--;
5891bef7707SMatthew Dillon } else if (node == DEVFS_MNTDATA(node->mp)->root_node) {
5901bef7707SMatthew Dillon DEVFS_MNTDATA(node->mp)->root_node = NULL;
5911bef7707SMatthew Dillon }
59221864bc5SMatthew Dillon node->flags &= ~DEVFS_NODE_LINKED;
59321864bc5SMatthew Dillon }
5948312ca30SAlex Hornung
5959e27c355SFrançois Tigeot /*
5969e27c355SFrançois Tigeot * Namecache invalidation.
597731fd4ccSMatthew Dillon *
5989e27c355SFrançois Tigeot * devfs alias nodes are special: their v_node entry is always null
599731fd4ccSMatthew Dillon * and they use the one from their link target. We thus use the
600731fd4ccSMatthew Dillon * target node's vp to invalidate both alias and target entries in
601731fd4ccSMatthew Dillon * the namecache.
602731fd4ccSMatthew Dillon *
6039e27c355SFrançois Tigeot * Doing so for the target is not necessary but it would be more
6049e27c355SFrançois Tigeot * expensive to resolve only the namecache entry of the alias node
6059e27c355SFrançois Tigeot * from the information available in this function.
606731fd4ccSMatthew Dillon *
607731fd4ccSMatthew Dillon * WARNING! We do not disassociate the vnode here. That can only
608731fd4ccSMatthew Dillon * be safely done in devfs_freep().
6099e27c355SFrançois Tigeot */
6101bef7707SMatthew Dillon if (node->node_type == Nlink) {
6111bef7707SMatthew Dillon if ((target = node->link_target) != NULL) {
61240c007d7SFrançois Tigeot vp = devfs_alias_getvp(node);
6131bef7707SMatthew Dillon node->link_target = NULL;
6141bef7707SMatthew Dillon target->nlinks--;
615770f8279SMatthew Dillon if (target->nlinks == 0 &&
616770f8279SMatthew Dillon (target->flags & DEVFS_DESTROYED)) {
617770f8279SMatthew Dillon devfs_freep(target);
618770f8279SMatthew Dillon }
6191bef7707SMatthew Dillon } else {
6201bef7707SMatthew Dillon vp = NULL;
6211bef7707SMatthew Dillon }
6221bef7707SMatthew Dillon } else {
6239e27c355SFrançois Tigeot vp = node->v_node;
6241bef7707SMatthew Dillon }
6259e27c355SFrançois Tigeot
6269e27c355SFrançois Tigeot if (vp != NULL)
6279e27c355SFrançois Tigeot cache_inval_vp(vp, CINV_DESTROY);
62821864bc5SMatthew Dillon }
62921864bc5SMatthew Dillon
63066abefa5SAlex Hornung void *
devfs_iterate_topology(struct devfs_node * node,devfs_iterate_callback_t * callback,void * arg1)63166abefa5SAlex Hornung devfs_iterate_topology(struct devfs_node *node,
63266abefa5SAlex Hornung devfs_iterate_callback_t *callback, void *arg1)
63321864bc5SMatthew Dillon {
63421864bc5SMatthew Dillon struct devfs_node *node1, *node2;
63566abefa5SAlex Hornung void *ret = NULL;
63621864bc5SMatthew Dillon
6371bef7707SMatthew Dillon if (((node->node_type == Nroot) || (node->node_type == Ndir)) &&
6381bef7707SMatthew Dillon node->nchildren > 2) {
639ca8d7677SMatthew Dillon TAILQ_FOREACH_MUTABLE(node1, DEVFS_DENODE_HEAD(node),
640ca8d7677SMatthew Dillon link, node2) {
6411bef7707SMatthew Dillon ret = devfs_iterate_topology(node1, callback, arg1);
6421bef7707SMatthew Dillon if (ret)
64366abefa5SAlex Hornung return ret;
64421864bc5SMatthew Dillon }
64521864bc5SMatthew Dillon }
64666abefa5SAlex Hornung ret = callback(node, arg1);
6471bef7707SMatthew Dillon
64866abefa5SAlex Hornung return ret;
64966abefa5SAlex Hornung }
65066abefa5SAlex Hornung
65140c007d7SFrançois Tigeot static void *
devfs_alias_reaper_callback(struct devfs_node * node,void * unused)65240c007d7SFrançois Tigeot devfs_alias_reaper_callback(struct devfs_node *node, void *unused)
65340c007d7SFrançois Tigeot {
65440c007d7SFrançois Tigeot if (node->node_type == Nlink) {
65540c007d7SFrançois Tigeot devfs_unlinkp(node);
65640c007d7SFrançois Tigeot devfs_freep(node);
65740c007d7SFrançois Tigeot }
65840c007d7SFrançois Tigeot
65940c007d7SFrançois Tigeot return NULL;
66040c007d7SFrançois Tigeot }
66140c007d7SFrançois Tigeot
66266abefa5SAlex Hornung /*
66366abefa5SAlex Hornung * devfs_reaperp() is a recursive function that iterates through all the
66466abefa5SAlex Hornung * topology, unlinking and freeing all devfs nodes.
66566abefa5SAlex Hornung */
66666abefa5SAlex Hornung static void *
devfs_reaperp_callback(struct devfs_node * node,void * unused)66766abefa5SAlex Hornung devfs_reaperp_callback(struct devfs_node *node, void *unused)
66866abefa5SAlex Hornung {
66921864bc5SMatthew Dillon devfs_unlinkp(node);
67021864bc5SMatthew Dillon devfs_freep(node);
67121864bc5SMatthew Dillon
67266abefa5SAlex Hornung return NULL;
67366abefa5SAlex Hornung }
67466abefa5SAlex Hornung
6751bef7707SMatthew Dillon /*
6761bef7707SMatthew Dillon * Report any orphans that we couldn't delete. The mp and mnt_data
6771bef7707SMatthew Dillon * are both disappearing, so we must also clean up the nodes a bit.
6781bef7707SMatthew Dillon */
6791bef7707SMatthew Dillon static void
devfs_iterate_orphans_unmount(struct mount * mp)6801bef7707SMatthew Dillon devfs_iterate_orphans_unmount(struct mount *mp)
6811bef7707SMatthew Dillon {
6821bef7707SMatthew Dillon struct devfs_orphan *orphan;
6831bef7707SMatthew Dillon
6841bef7707SMatthew Dillon while ((orphan = TAILQ_FIRST(DEVFS_ORPHANLIST(mp))) != NULL) {
6851bef7707SMatthew Dillon devfs_freep(orphan->node);
6861bef7707SMatthew Dillon /* orphan stale */
6871bef7707SMatthew Dillon }
6881bef7707SMatthew Dillon }
6891bef7707SMatthew Dillon
69066abefa5SAlex Hornung static void *
devfs_gc_dirs_callback(struct devfs_node * node,void * unused)69166abefa5SAlex Hornung devfs_gc_dirs_callback(struct devfs_node *node, void *unused)
69266abefa5SAlex Hornung {
6938e78a293SSascha Wildner if (node->node_type == Ndir) {
69439a08947SAlex Hornung if ((node->nchildren == 2) &&
69539a08947SAlex Hornung !(node->flags & DEVFS_USER_CREATED)) {
69666abefa5SAlex Hornung devfs_unlinkp(node);
69766abefa5SAlex Hornung devfs_freep(node);
69866abefa5SAlex Hornung }
69966abefa5SAlex Hornung }
70066abefa5SAlex Hornung
70166abefa5SAlex Hornung return NULL;
70266abefa5SAlex Hornung }
70366abefa5SAlex Hornung
70466abefa5SAlex Hornung static void *
devfs_gc_links_callback(struct devfs_node * node,struct devfs_node * target)70566abefa5SAlex Hornung devfs_gc_links_callback(struct devfs_node *node, struct devfs_node *target)
70666abefa5SAlex Hornung {
7078e78a293SSascha Wildner if ((node->node_type == Nlink) && (node->link_target == target)) {
70866abefa5SAlex Hornung devfs_unlinkp(node);
70966abefa5SAlex Hornung devfs_freep(node);
71066abefa5SAlex Hornung }
71166abefa5SAlex Hornung
71266abefa5SAlex Hornung return NULL;
71321864bc5SMatthew Dillon }
71421864bc5SMatthew Dillon
71521864bc5SMatthew Dillon /*
716ca8d7677SMatthew Dillon * devfs_gc() is devfs garbage collector. It takes care of unlinking and
717ca8d7677SMatthew Dillon * freeing a node, but also removes empty directories and links that link
718ca8d7677SMatthew Dillon * via devfs auto-link mechanism to the node being deleted.
71921864bc5SMatthew Dillon */
72021864bc5SMatthew Dillon int
devfs_gc(struct devfs_node * node)72121864bc5SMatthew Dillon devfs_gc(struct devfs_node *node)
72221864bc5SMatthew Dillon {
72321864bc5SMatthew Dillon struct devfs_node *root_node = DEVFS_MNTDATA(node->mp)->root_node;
72421864bc5SMatthew Dillon
72566abefa5SAlex Hornung if (node->nlinks > 0)
72666abefa5SAlex Hornung devfs_iterate_topology(root_node,
72766abefa5SAlex Hornung (devfs_iterate_callback_t *)devfs_gc_links_callback, node);
72866abefa5SAlex Hornung
72921864bc5SMatthew Dillon devfs_unlinkp(node);
73066abefa5SAlex Hornung devfs_iterate_topology(root_node,
73166abefa5SAlex Hornung (devfs_iterate_callback_t *)devfs_gc_dirs_callback, NULL);
73221864bc5SMatthew Dillon
73321864bc5SMatthew Dillon devfs_freep(node);
73421864bc5SMatthew Dillon
73521864bc5SMatthew Dillon return 0;
73621864bc5SMatthew Dillon }
73721864bc5SMatthew Dillon
73821864bc5SMatthew Dillon /*
739ca8d7677SMatthew Dillon * devfs_create_dev() is the asynchronous entry point for device creation.
740ca8d7677SMatthew Dillon * It just sends a message with the relevant details to the devfs core.
741ca8d7677SMatthew Dillon *
742ca8d7677SMatthew Dillon * This function will reference the passed device. The reference is owned
743ca8d7677SMatthew Dillon * by devfs and represents all of the device's node associations.
74421864bc5SMatthew Dillon */
74521864bc5SMatthew Dillon int
devfs_create_dev(cdev_t dev,uid_t uid,gid_t gid,int perms)74621864bc5SMatthew Dillon devfs_create_dev(cdev_t dev, uid_t uid, gid_t gid, int perms)
74721864bc5SMatthew Dillon {
748ca8d7677SMatthew Dillon reference_dev(dev);
749bc185c5aSAlex Hornung devfs_msg_send_dev(DEVFS_DEVICE_CREATE, dev, uid, gid, perms);
750bc185c5aSAlex Hornung
75121864bc5SMatthew Dillon return 0;
75221864bc5SMatthew Dillon }
75321864bc5SMatthew Dillon
75421864bc5SMatthew Dillon /*
755ca8d7677SMatthew Dillon * devfs_destroy_dev() is the asynchronous entry point for device destruction.
756ca8d7677SMatthew Dillon * It just sends a message with the relevant details to the devfs core.
75721864bc5SMatthew Dillon */
75821864bc5SMatthew Dillon int
devfs_destroy_dev(cdev_t dev)75921864bc5SMatthew Dillon devfs_destroy_dev(cdev_t dev)
76021864bc5SMatthew Dillon {
76121864bc5SMatthew Dillon devfs_msg_send_dev(DEVFS_DEVICE_DESTROY, dev, 0, 0, 0);
76221864bc5SMatthew Dillon return 0;
76321864bc5SMatthew Dillon }
76421864bc5SMatthew Dillon
76521864bc5SMatthew Dillon /*
766ca8d7677SMatthew Dillon * devfs_mount_add() is the synchronous entry point for adding a new devfs
767ca8d7677SMatthew Dillon * mount. It sends a synchronous message with the relevant details to the
768ca8d7677SMatthew Dillon * devfs core.
76921864bc5SMatthew Dillon */
77021864bc5SMatthew Dillon int
devfs_mount_add(struct devfs_mnt_data * mnt)77121864bc5SMatthew Dillon devfs_mount_add(struct devfs_mnt_data *mnt)
77221864bc5SMatthew Dillon {
77321864bc5SMatthew Dillon devfs_msg_t msg;
77421864bc5SMatthew Dillon
77521864bc5SMatthew Dillon msg = devfs_msg_get();
776ca8d7677SMatthew Dillon msg->mdv_mnt = mnt;
7772e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_MOUNT_ADD, msg);
77821864bc5SMatthew Dillon devfs_msg_put(msg);
77921864bc5SMatthew Dillon
78021864bc5SMatthew Dillon return 0;
78121864bc5SMatthew Dillon }
78221864bc5SMatthew Dillon
78321864bc5SMatthew Dillon /*
78421864bc5SMatthew Dillon * devfs_mount_del() is the synchronous entry point for removing a devfs mount.
78521864bc5SMatthew Dillon * It sends a synchronous message with the relevant details to the devfs core.
78621864bc5SMatthew Dillon */
78721864bc5SMatthew Dillon int
devfs_mount_del(struct devfs_mnt_data * mnt)78821864bc5SMatthew Dillon devfs_mount_del(struct devfs_mnt_data *mnt)
78921864bc5SMatthew Dillon {
79021864bc5SMatthew Dillon devfs_msg_t msg;
79121864bc5SMatthew Dillon
79221864bc5SMatthew Dillon msg = devfs_msg_get();
793ca8d7677SMatthew Dillon msg->mdv_mnt = mnt;
7942e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_MOUNT_DEL, msg);
79521864bc5SMatthew Dillon devfs_msg_put(msg);
79621864bc5SMatthew Dillon
79721864bc5SMatthew Dillon return 0;
79821864bc5SMatthew Dillon }
79921864bc5SMatthew Dillon
80021864bc5SMatthew Dillon /*
80172ea429eSAlex Hornung * devfs_destroy_related() is the synchronous entry point for device
802bc185c5aSAlex Hornung * destruction by subname. It just sends a message with the relevant details to
803bc185c5aSAlex Hornung * the devfs core.
80421864bc5SMatthew Dillon */
80521864bc5SMatthew Dillon int
devfs_destroy_related(cdev_t dev)80672ea429eSAlex Hornung devfs_destroy_related(cdev_t dev)
80721864bc5SMatthew Dillon {
808ca8d7677SMatthew Dillon devfs_msg_t msg;
809ca8d7677SMatthew Dillon
810ca8d7677SMatthew Dillon msg = devfs_msg_get();
81172ea429eSAlex Hornung msg->mdv_load = dev;
8122e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_DESTROY_RELATED, msg);
813ca8d7677SMatthew Dillon devfs_msg_put(msg);
814ca8d7677SMatthew Dillon return 0;
815ca8d7677SMatthew Dillon }
816ca8d7677SMatthew Dillon
817ca8d7677SMatthew Dillon int
devfs_clr_related_flag(cdev_t dev,uint32_t flag)81872ea429eSAlex Hornung devfs_clr_related_flag(cdev_t dev, uint32_t flag)
819ca8d7677SMatthew Dillon {
820ca8d7677SMatthew Dillon devfs_msg_t msg;
821ca8d7677SMatthew Dillon
822ca8d7677SMatthew Dillon msg = devfs_msg_get();
82372ea429eSAlex Hornung msg->mdv_flags.dev = dev;
824ca8d7677SMatthew Dillon msg->mdv_flags.flag = flag;
8252e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_CLR_RELATED_FLAG, msg);
826ca8d7677SMatthew Dillon devfs_msg_put(msg);
827ca8d7677SMatthew Dillon
828ca8d7677SMatthew Dillon return 0;
829ca8d7677SMatthew Dillon }
830ca8d7677SMatthew Dillon
831ca8d7677SMatthew Dillon int
devfs_destroy_related_without_flag(cdev_t dev,uint32_t flag)83272ea429eSAlex Hornung devfs_destroy_related_without_flag(cdev_t dev, uint32_t flag)
833ca8d7677SMatthew Dillon {
834ca8d7677SMatthew Dillon devfs_msg_t msg;
835ca8d7677SMatthew Dillon
836ca8d7677SMatthew Dillon msg = devfs_msg_get();
83772ea429eSAlex Hornung msg->mdv_flags.dev = dev;
838ca8d7677SMatthew Dillon msg->mdv_flags.flag = flag;
8392e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_DESTROY_RELATED_WO_FLAG, msg);
840ca8d7677SMatthew Dillon devfs_msg_put(msg);
841ca8d7677SMatthew Dillon
84221864bc5SMatthew Dillon return 0;
84321864bc5SMatthew Dillon }
84421864bc5SMatthew Dillon
84521864bc5SMatthew Dillon /*
846ca8d7677SMatthew Dillon * devfs_create_all_dev is the asynchronous entry point to trigger device
847ca8d7677SMatthew Dillon * node creation. It just sends a message with the relevant details to
848ca8d7677SMatthew Dillon * the devfs core.
84921864bc5SMatthew Dillon */
85021864bc5SMatthew Dillon int
devfs_create_all_dev(struct devfs_node * root)85121864bc5SMatthew Dillon devfs_create_all_dev(struct devfs_node *root)
85221864bc5SMatthew Dillon {
85321864bc5SMatthew Dillon devfs_msg_send_generic(DEVFS_CREATE_ALL_DEV, root);
85421864bc5SMatthew Dillon return 0;
85521864bc5SMatthew Dillon }
85621864bc5SMatthew Dillon
85721864bc5SMatthew Dillon /*
858ca8d7677SMatthew Dillon * devfs_destroy_dev_by_ops is the asynchronous entry point to destroy all
859ca8d7677SMatthew Dillon * devices with a specific set of dev_ops and minor. It just sends a
860ca8d7677SMatthew Dillon * message with the relevant details to the devfs core.
86121864bc5SMatthew Dillon */
86221864bc5SMatthew Dillon int
devfs_destroy_dev_by_ops(struct dev_ops * ops,int minor)86321864bc5SMatthew Dillon devfs_destroy_dev_by_ops(struct dev_ops *ops, int minor)
86421864bc5SMatthew Dillon {
86521864bc5SMatthew Dillon devfs_msg_send_ops(DEVFS_DESTROY_DEV_BY_OPS, ops, minor);
86621864bc5SMatthew Dillon return 0;
86721864bc5SMatthew Dillon }
86821864bc5SMatthew Dillon
86921864bc5SMatthew Dillon /*
870ca8d7677SMatthew Dillon * devfs_clone_handler_add is the synchronous entry point to add a new
871ca8d7677SMatthew Dillon * clone handler. It just sends a message with the relevant details to
872ca8d7677SMatthew Dillon * the devfs core.
87321864bc5SMatthew Dillon */
87421864bc5SMatthew Dillon int
devfs_clone_handler_add(const char * name,d_clone_t * nhandler)87507dfa375SAlex Hornung devfs_clone_handler_add(const char *name, d_clone_t *nhandler)
87621864bc5SMatthew Dillon {
877ca8d7677SMatthew Dillon devfs_msg_t msg;
878ca8d7677SMatthew Dillon
879ca8d7677SMatthew Dillon msg = devfs_msg_get();
880ca8d7677SMatthew Dillon msg->mdv_chandler.name = name;
881ca8d7677SMatthew Dillon msg->mdv_chandler.nhandler = nhandler;
8822e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_CHANDLER_ADD, msg);
883ca8d7677SMatthew Dillon devfs_msg_put(msg);
88421864bc5SMatthew Dillon return 0;
88521864bc5SMatthew Dillon }
88621864bc5SMatthew Dillon
88721864bc5SMatthew Dillon /*
888ca8d7677SMatthew Dillon * devfs_clone_handler_del is the synchronous entry point to remove a
889ca8d7677SMatthew Dillon * clone handler. It just sends a message with the relevant details to
890ca8d7677SMatthew Dillon * the devfs core.
89121864bc5SMatthew Dillon */
89221864bc5SMatthew Dillon int
devfs_clone_handler_del(const char * name)89307dfa375SAlex Hornung devfs_clone_handler_del(const char *name)
89421864bc5SMatthew Dillon {
895ca8d7677SMatthew Dillon devfs_msg_t msg;
896ca8d7677SMatthew Dillon
897ca8d7677SMatthew Dillon msg = devfs_msg_get();
898ca8d7677SMatthew Dillon msg->mdv_chandler.name = name;
899ca8d7677SMatthew Dillon msg->mdv_chandler.nhandler = NULL;
9002e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_CHANDLER_DEL, msg);
901ca8d7677SMatthew Dillon devfs_msg_put(msg);
90221864bc5SMatthew Dillon return 0;
90321864bc5SMatthew Dillon }
90421864bc5SMatthew Dillon
90521864bc5SMatthew Dillon /*
906ca8d7677SMatthew Dillon * devfs_find_device_by_name is the synchronous entry point to find a
907ca8d7677SMatthew Dillon * device given its name. It sends a synchronous message with the
908ca8d7677SMatthew Dillon * relevant details to the devfs core and returns the answer.
90921864bc5SMatthew Dillon */
91021864bc5SMatthew Dillon cdev_t
devfs_find_device_by_name(const char * fmt,...)91121864bc5SMatthew Dillon devfs_find_device_by_name(const char *fmt, ...)
91221864bc5SMatthew Dillon {
91321864bc5SMatthew Dillon cdev_t found = NULL;
91421864bc5SMatthew Dillon devfs_msg_t msg;
915da655383SMatthew Dillon char *target;
91621864bc5SMatthew Dillon __va_list ap;
91721864bc5SMatthew Dillon
91821864bc5SMatthew Dillon if (fmt == NULL)
91921864bc5SMatthew Dillon return NULL;
92021864bc5SMatthew Dillon
92121864bc5SMatthew Dillon __va_start(ap, fmt);
922ae71ac09Szrj kvasnprintf(&target, PATH_MAX, fmt, ap);
92321864bc5SMatthew Dillon __va_end(ap);
92421864bc5SMatthew Dillon
92521864bc5SMatthew Dillon msg = devfs_msg_get();
926ca8d7677SMatthew Dillon msg->mdv_name = target;
9272e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_FIND_DEVICE_BY_NAME, msg);
928ca8d7677SMatthew Dillon found = msg->mdv_cdev;
92921864bc5SMatthew Dillon devfs_msg_put(msg);
930da655383SMatthew Dillon kvasfree(&target);
93121864bc5SMatthew Dillon
93221864bc5SMatthew Dillon return found;
93321864bc5SMatthew Dillon }
93421864bc5SMatthew Dillon
93521864bc5SMatthew Dillon /*
9362ac7d105SSascha Wildner * devfs_find_device_by_devid is the synchronous entry point to find a
937ca8d7677SMatthew Dillon * device given its udev number. It sends a synchronous message with
938ca8d7677SMatthew Dillon * the relevant details to the devfs core and returns the answer.
93921864bc5SMatthew Dillon */
94021864bc5SMatthew Dillon cdev_t
devfs_find_device_by_devid(dev_t udev)9412ac7d105SSascha Wildner devfs_find_device_by_devid(dev_t udev)
94221864bc5SMatthew Dillon {
94321864bc5SMatthew Dillon cdev_t found = NULL;
94421864bc5SMatthew Dillon devfs_msg_t msg;
94521864bc5SMatthew Dillon
94621864bc5SMatthew Dillon msg = devfs_msg_get();
947ca8d7677SMatthew Dillon msg->mdv_udev = udev;
9482ac7d105SSascha Wildner devfs_msg_send_sync(DEVFS_FIND_DEVICE_BY_DEVID, msg);
949ca8d7677SMatthew Dillon found = msg->mdv_cdev;
95021864bc5SMatthew Dillon devfs_msg_put(msg);
95121864bc5SMatthew Dillon
952ca8d7677SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG,
9532ac7d105SSascha Wildner "devfs_find_device_by_devid found? %s -end:3-\n",
954ca8d7677SMatthew Dillon ((found) ? found->si_name:"NO"));
95521864bc5SMatthew Dillon return found;
95621864bc5SMatthew Dillon }
95721864bc5SMatthew Dillon
958fa7e6f37SAlex Hornung struct vnode *
devfs_inode_to_vnode(struct mount * mp,ino_t target)959fa7e6f37SAlex Hornung devfs_inode_to_vnode(struct mount *mp, ino_t target)
960fa7e6f37SAlex Hornung {
961fa7e6f37SAlex Hornung struct vnode *vp = NULL;
962fa7e6f37SAlex Hornung devfs_msg_t msg;
963fa7e6f37SAlex Hornung
964fa7e6f37SAlex Hornung if (mp == NULL)
965fa7e6f37SAlex Hornung return NULL;
966fa7e6f37SAlex Hornung
967fa7e6f37SAlex Hornung msg = devfs_msg_get();
968fa7e6f37SAlex Hornung msg->mdv_ino.mp = mp;
969fa7e6f37SAlex Hornung msg->mdv_ino.ino = target;
9702e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_INODE_TO_VNODE, msg);
971fa7e6f37SAlex Hornung vp = msg->mdv_ino.vp;
972fa7e6f37SAlex Hornung vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
973fa7e6f37SAlex Hornung devfs_msg_put(msg);
974fa7e6f37SAlex Hornung
975fa7e6f37SAlex Hornung return vp;
976fa7e6f37SAlex Hornung }
977fa7e6f37SAlex Hornung
97821864bc5SMatthew Dillon /*
979ca8d7677SMatthew Dillon * devfs_make_alias is the asynchronous entry point to register an alias
980ca8d7677SMatthew Dillon * for a device. It just sends a message with the relevant details to the
981ca8d7677SMatthew Dillon * devfs core.
98221864bc5SMatthew Dillon */
98321864bc5SMatthew Dillon int
devfs_make_alias(const char * name,cdev_t dev_target)98407dfa375SAlex Hornung devfs_make_alias(const char *name, cdev_t dev_target)
98521864bc5SMatthew Dillon {
986ca8d7677SMatthew Dillon struct devfs_alias *alias;
9875298e788SAlex Hornung size_t len;
9885298e788SAlex Hornung
9895298e788SAlex Hornung len = strlen(name);
990ca8d7677SMatthew Dillon
991ca8d7677SMatthew Dillon alias = kmalloc(sizeof(struct devfs_alias), M_DEVFS, M_WAITOK);
99207dfa375SAlex Hornung alias->name = kstrdup(name, M_DEVFS);
9935298e788SAlex Hornung alias->namlen = len;
99421864bc5SMatthew Dillon alias->dev_target = dev_target;
99521864bc5SMatthew Dillon
99621864bc5SMatthew Dillon devfs_msg_send_generic(DEVFS_MAKE_ALIAS, alias);
99721864bc5SMatthew Dillon return 0;
99821864bc5SMatthew Dillon }
99921864bc5SMatthew Dillon
100021864bc5SMatthew Dillon /*
10018312ca30SAlex Hornung * devfs_destroy_alias is the asynchronous entry point to deregister an alias
10028312ca30SAlex Hornung * for a device. It just sends a message with the relevant details to the
10038312ca30SAlex Hornung * devfs core.
10048312ca30SAlex Hornung */
10058312ca30SAlex Hornung int
devfs_destroy_alias(const char * name,cdev_t dev_target)10068312ca30SAlex Hornung devfs_destroy_alias(const char *name, cdev_t dev_target)
10078312ca30SAlex Hornung {
10088312ca30SAlex Hornung struct devfs_alias *alias;
10098312ca30SAlex Hornung size_t len;
10108312ca30SAlex Hornung
10118312ca30SAlex Hornung len = strlen(name);
10128312ca30SAlex Hornung
10138312ca30SAlex Hornung alias = kmalloc(sizeof(struct devfs_alias), M_DEVFS, M_WAITOK);
10148312ca30SAlex Hornung alias->name = kstrdup(name, M_DEVFS);
10158312ca30SAlex Hornung alias->namlen = len;
10168312ca30SAlex Hornung alias->dev_target = dev_target;
10178312ca30SAlex Hornung
10188312ca30SAlex Hornung devfs_msg_send_generic(DEVFS_DESTROY_ALIAS, alias);
10198312ca30SAlex Hornung return 0;
10208312ca30SAlex Hornung }
10218312ca30SAlex Hornung
10228312ca30SAlex Hornung /*
1023ca8d7677SMatthew Dillon * devfs_apply_rules is the asynchronous entry point to trigger application
1024ca8d7677SMatthew Dillon * of all rules. It just sends a message with the relevant details to the
1025ca8d7677SMatthew Dillon * devfs core.
102621864bc5SMatthew Dillon */
102721864bc5SMatthew Dillon int
devfs_apply_rules(char * mntto)102821864bc5SMatthew Dillon devfs_apply_rules(char *mntto)
102921864bc5SMatthew Dillon {
103021864bc5SMatthew Dillon char *new_name;
103121864bc5SMatthew Dillon
103207dfa375SAlex Hornung new_name = kstrdup(mntto, M_DEVFS);
103321864bc5SMatthew Dillon devfs_msg_send_name(DEVFS_APPLY_RULES, new_name);
1034bc185c5aSAlex Hornung
103521864bc5SMatthew Dillon return 0;
103621864bc5SMatthew Dillon }
103721864bc5SMatthew Dillon
103821864bc5SMatthew Dillon /*
1039bc185c5aSAlex Hornung * devfs_reset_rules is the asynchronous entry point to trigger reset of all
1040bc185c5aSAlex Hornung * rules. It just sends a message with the relevant details to the devfs core.
104121864bc5SMatthew Dillon */
104221864bc5SMatthew Dillon int
devfs_reset_rules(char * mntto)104321864bc5SMatthew Dillon devfs_reset_rules(char *mntto)
104421864bc5SMatthew Dillon {
104521864bc5SMatthew Dillon char *new_name;
104621864bc5SMatthew Dillon
104707dfa375SAlex Hornung new_name = kstrdup(mntto, M_DEVFS);
104821864bc5SMatthew Dillon devfs_msg_send_name(DEVFS_RESET_RULES, new_name);
1049bc185c5aSAlex Hornung
105021864bc5SMatthew Dillon return 0;
105121864bc5SMatthew Dillon }
105221864bc5SMatthew Dillon
105321864bc5SMatthew Dillon
105421864bc5SMatthew Dillon /*
105521864bc5SMatthew Dillon * devfs_scan_callback is the asynchronous entry point to call a callback
105621864bc5SMatthew Dillon * on all cdevs.
105721864bc5SMatthew Dillon * It just sends a message with the relevant details to the devfs core.
105821864bc5SMatthew Dillon */
105921864bc5SMatthew Dillon int
devfs_scan_callback(devfs_scan_t * callback,void * arg)10603a3826b3SAlex Hornung devfs_scan_callback(devfs_scan_t *callback, void *arg)
106121864bc5SMatthew Dillon {
106221864bc5SMatthew Dillon devfs_msg_t msg;
106321864bc5SMatthew Dillon
10642d7c4a1eSSascha Wildner KKASSERT(callback);
106521864bc5SMatthew Dillon
106621864bc5SMatthew Dillon msg = devfs_msg_get();
1067ca8d7677SMatthew Dillon msg->mdv_load = callback;
10683a3826b3SAlex Hornung msg->mdv_load2 = arg;
10692e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_SCAN_CALLBACK, msg);
107021864bc5SMatthew Dillon devfs_msg_put(msg);
107121864bc5SMatthew Dillon
107221864bc5SMatthew Dillon return 0;
107321864bc5SMatthew Dillon }
107421864bc5SMatthew Dillon
107521864bc5SMatthew Dillon
107621864bc5SMatthew Dillon /*
1077bc185c5aSAlex Hornung * Acts as a message drain. Any message that is replied to here gets destroyed
1078bc185c5aSAlex Hornung * and the memory freed.
107921864bc5SMatthew Dillon */
108021864bc5SMatthew Dillon static void
devfs_msg_autofree_reply(lwkt_port_t port,lwkt_msg_t msg)108121864bc5SMatthew Dillon devfs_msg_autofree_reply(lwkt_port_t port, lwkt_msg_t msg)
108221864bc5SMatthew Dillon {
108321864bc5SMatthew Dillon devfs_msg_put((devfs_msg_t)msg);
108421864bc5SMatthew Dillon }
108521864bc5SMatthew Dillon
108621864bc5SMatthew Dillon /*
108721864bc5SMatthew Dillon * devfs_msg_get allocates a new devfs msg and returns it.
108821864bc5SMatthew Dillon */
108921864bc5SMatthew Dillon devfs_msg_t
devfs_msg_get(void)1090b815579bSSascha Wildner devfs_msg_get(void)
109121864bc5SMatthew Dillon {
109221864bc5SMatthew Dillon return objcache_get(devfs_msg_cache, M_WAITOK);
109321864bc5SMatthew Dillon }
109421864bc5SMatthew Dillon
109521864bc5SMatthew Dillon /*
109621864bc5SMatthew Dillon * devfs_msg_put deallocates a given devfs msg.
109721864bc5SMatthew Dillon */
109821864bc5SMatthew Dillon int
devfs_msg_put(devfs_msg_t msg)109921864bc5SMatthew Dillon devfs_msg_put(devfs_msg_t msg)
110021864bc5SMatthew Dillon {
110121864bc5SMatthew Dillon objcache_put(devfs_msg_cache, msg);
110221864bc5SMatthew Dillon return 0;
110321864bc5SMatthew Dillon }
110421864bc5SMatthew Dillon
110521864bc5SMatthew Dillon /*
110621864bc5SMatthew Dillon * devfs_msg_send is the generic asynchronous message sending facility
110721864bc5SMatthew Dillon * for devfs. By default the reply port is the automatic disposal port.
1108d0fe8596SMatthew Dillon *
1109d0fe8596SMatthew Dillon * If the current thread is the devfs_msg_port thread we execute the
1110d0fe8596SMatthew Dillon * operation synchronously.
111121864bc5SMatthew Dillon */
1112d0fe8596SMatthew Dillon void
devfs_msg_send(uint32_t cmd,devfs_msg_t devfs_msg)111321864bc5SMatthew Dillon devfs_msg_send(uint32_t cmd, devfs_msg_t devfs_msg)
111421864bc5SMatthew Dillon {
111521864bc5SMatthew Dillon lwkt_port_t port = &devfs_msg_port;
111621864bc5SMatthew Dillon
111721864bc5SMatthew Dillon lwkt_initmsg(&devfs_msg->hdr, &devfs_dispose_port, 0);
111821864bc5SMatthew Dillon
111921864bc5SMatthew Dillon devfs_msg->hdr.u.ms_result = cmd;
112021864bc5SMatthew Dillon
1121d0fe8596SMatthew Dillon if (port->mpu_td == curthread) {
1122d0fe8596SMatthew Dillon devfs_msg_exec(devfs_msg);
1123d0fe8596SMatthew Dillon lwkt_replymsg(&devfs_msg->hdr, 0);
1124d0fe8596SMatthew Dillon } else {
112521864bc5SMatthew Dillon lwkt_sendmsg(port, (lwkt_msg_t)devfs_msg);
1126d0fe8596SMatthew Dillon }
112721864bc5SMatthew Dillon }
112821864bc5SMatthew Dillon
112921864bc5SMatthew Dillon /*
113021864bc5SMatthew Dillon * devfs_msg_send_sync is the generic synchronous message sending
113121864bc5SMatthew Dillon * facility for devfs. It initializes a local reply port and waits
11322e05d283SNuno Antunes * for the core's answer. The core will write the answer on the same
11332e05d283SNuno Antunes * message which is sent back as reply. The caller still has a reference
11342e05d283SNuno Antunes * to the message, so we don't need to return it.
113521864bc5SMatthew Dillon */
11362e05d283SNuno Antunes int
devfs_msg_send_sync(uint32_t cmd,devfs_msg_t devfs_msg)113721864bc5SMatthew Dillon devfs_msg_send_sync(uint32_t cmd, devfs_msg_t devfs_msg)
113821864bc5SMatthew Dillon {
113921864bc5SMatthew Dillon struct lwkt_port rep_port;
11402e05d283SNuno Antunes int error;
114121864bc5SMatthew Dillon lwkt_port_t port = &devfs_msg_port;
114221864bc5SMatthew Dillon
114321864bc5SMatthew Dillon lwkt_initport_thread(&rep_port, curthread);
114421864bc5SMatthew Dillon lwkt_initmsg(&devfs_msg->hdr, &rep_port, 0);
114521864bc5SMatthew Dillon
114621864bc5SMatthew Dillon devfs_msg->hdr.u.ms_result = cmd;
114721864bc5SMatthew Dillon
11482e05d283SNuno Antunes error = lwkt_domsg(port, (lwkt_msg_t)devfs_msg, 0);
114921864bc5SMatthew Dillon
11502e05d283SNuno Antunes return error;
115121864bc5SMatthew Dillon }
115221864bc5SMatthew Dillon
115321864bc5SMatthew Dillon /*
115421864bc5SMatthew Dillon * sends a message with a generic argument.
115521864bc5SMatthew Dillon */
1156d0fe8596SMatthew Dillon void
devfs_msg_send_generic(uint32_t cmd,void * load)115721864bc5SMatthew Dillon devfs_msg_send_generic(uint32_t cmd, void *load)
115821864bc5SMatthew Dillon {
115921864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
116021864bc5SMatthew Dillon
1161d0fe8596SMatthew Dillon devfs_msg->mdv_load = load;
1162d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
116321864bc5SMatthew Dillon }
116421864bc5SMatthew Dillon
116521864bc5SMatthew Dillon /*
116621864bc5SMatthew Dillon * sends a message with a name argument.
116721864bc5SMatthew Dillon */
1168d0fe8596SMatthew Dillon void
devfs_msg_send_name(uint32_t cmd,char * name)116921864bc5SMatthew Dillon devfs_msg_send_name(uint32_t cmd, char *name)
117021864bc5SMatthew Dillon {
117121864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
117221864bc5SMatthew Dillon
1173d0fe8596SMatthew Dillon devfs_msg->mdv_name = name;
1174d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
117521864bc5SMatthew Dillon }
117621864bc5SMatthew Dillon
117721864bc5SMatthew Dillon /*
117821864bc5SMatthew Dillon * sends a message with a mount argument.
117921864bc5SMatthew Dillon */
1180d0fe8596SMatthew Dillon void
devfs_msg_send_mount(uint32_t cmd,struct devfs_mnt_data * mnt)118121864bc5SMatthew Dillon devfs_msg_send_mount(uint32_t cmd, struct devfs_mnt_data *mnt)
118221864bc5SMatthew Dillon {
118321864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
118421864bc5SMatthew Dillon
1185d0fe8596SMatthew Dillon devfs_msg->mdv_mnt = mnt;
1186d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
118721864bc5SMatthew Dillon }
118821864bc5SMatthew Dillon
118921864bc5SMatthew Dillon /*
119021864bc5SMatthew Dillon * sends a message with an ops argument.
119121864bc5SMatthew Dillon */
1192d0fe8596SMatthew Dillon void
devfs_msg_send_ops(uint32_t cmd,struct dev_ops * ops,int minor)119321864bc5SMatthew Dillon devfs_msg_send_ops(uint32_t cmd, struct dev_ops *ops, int minor)
119421864bc5SMatthew Dillon {
119521864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
1196d0fe8596SMatthew Dillon
1197ca8d7677SMatthew Dillon devfs_msg->mdv_ops.ops = ops;
1198ca8d7677SMatthew Dillon devfs_msg->mdv_ops.minor = minor;
1199d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
120021864bc5SMatthew Dillon }
120121864bc5SMatthew Dillon
120221864bc5SMatthew Dillon /*
120321864bc5SMatthew Dillon * sends a message with a clone handler argument.
120421864bc5SMatthew Dillon */
1205d0fe8596SMatthew Dillon void
devfs_msg_send_chandler(uint32_t cmd,char * name,d_clone_t handler)120621864bc5SMatthew Dillon devfs_msg_send_chandler(uint32_t cmd, char *name, d_clone_t handler)
120721864bc5SMatthew Dillon {
120821864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
1209d0fe8596SMatthew Dillon
1210ca8d7677SMatthew Dillon devfs_msg->mdv_chandler.name = name;
1211ca8d7677SMatthew Dillon devfs_msg->mdv_chandler.nhandler = handler;
1212d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
121321864bc5SMatthew Dillon }
121421864bc5SMatthew Dillon
121521864bc5SMatthew Dillon /*
121621864bc5SMatthew Dillon * sends a message with a device argument.
121721864bc5SMatthew Dillon */
1218d0fe8596SMatthew Dillon void
devfs_msg_send_dev(uint32_t cmd,cdev_t dev,uid_t uid,gid_t gid,int perms)121921864bc5SMatthew Dillon devfs_msg_send_dev(uint32_t cmd, cdev_t dev, uid_t uid, gid_t gid, int perms)
122021864bc5SMatthew Dillon {
122121864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
1222d0fe8596SMatthew Dillon
1223ca8d7677SMatthew Dillon devfs_msg->mdv_dev.dev = dev;
1224ca8d7677SMatthew Dillon devfs_msg->mdv_dev.uid = uid;
1225ca8d7677SMatthew Dillon devfs_msg->mdv_dev.gid = gid;
1226ca8d7677SMatthew Dillon devfs_msg->mdv_dev.perms = perms;
122721864bc5SMatthew Dillon
1228d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
122921864bc5SMatthew Dillon }
123021864bc5SMatthew Dillon
123121864bc5SMatthew Dillon /*
123221864bc5SMatthew Dillon * sends a message with a link argument.
123321864bc5SMatthew Dillon */
1234d0fe8596SMatthew Dillon void
devfs_msg_send_link(uint32_t cmd,char * name,char * target,struct mount * mp)123521864bc5SMatthew Dillon devfs_msg_send_link(uint32_t cmd, char *name, char *target, struct mount *mp)
123621864bc5SMatthew Dillon {
123721864bc5SMatthew Dillon devfs_msg_t devfs_msg = devfs_msg_get();
1238d0fe8596SMatthew Dillon
1239ca8d7677SMatthew Dillon devfs_msg->mdv_link.name = name;
1240ca8d7677SMatthew Dillon devfs_msg->mdv_link.target = target;
1241ca8d7677SMatthew Dillon devfs_msg->mdv_link.mp = mp;
1242d0fe8596SMatthew Dillon devfs_msg_send(cmd, devfs_msg);
124321864bc5SMatthew Dillon }
124421864bc5SMatthew Dillon
124521864bc5SMatthew Dillon /*
124621864bc5SMatthew Dillon * devfs_msg_core is the main devfs thread. It handles all incoming messages
124721864bc5SMatthew Dillon * and calls the relevant worker functions. By using messages it's assured
124821864bc5SMatthew Dillon * that events occur in the correct order.
124921864bc5SMatthew Dillon */
125021864bc5SMatthew Dillon static void
devfs_msg_core(void * arg)125121864bc5SMatthew Dillon devfs_msg_core(void *arg)
125221864bc5SMatthew Dillon {
125321864bc5SMatthew Dillon devfs_msg_t msg;
125421864bc5SMatthew Dillon
125521864bc5SMatthew Dillon lwkt_initport_thread(&devfs_msg_port, curthread);
1256c9e9fb21SMatthew Dillon
1257c9e9fb21SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
1258c9e9fb21SMatthew Dillon devfs_run = 1;
1259bc185c5aSAlex Hornung wakeup(td_core);
1260c9e9fb21SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
1261c9e9fb21SMatthew Dillon
1262c0739b3cSMatthew Dillon lwkt_gettoken(&devfs_token);
126321864bc5SMatthew Dillon
1264d0fe8596SMatthew Dillon while (devfs_run) {
126521864bc5SMatthew Dillon msg = (devfs_msg_t)lwkt_waitport(&devfs_msg_port, 0);
1266bc185c5aSAlex Hornung devfs_debug(DEVFS_DEBUG_DEBUG,
1267d0fe8596SMatthew Dillon "devfs_msg_core, new msg: %x\n",
1268d0fe8596SMatthew Dillon (unsigned int)msg->hdr.u.ms_result);
1269d0fe8596SMatthew Dillon devfs_msg_exec(msg);
1270d0fe8596SMatthew Dillon lwkt_replymsg(&msg->hdr, 0);
1271d0fe8596SMatthew Dillon }
1272c9e9fb21SMatthew Dillon
1273c0739b3cSMatthew Dillon lwkt_reltoken(&devfs_token);
1274d0fe8596SMatthew Dillon wakeup(td_core);
1275c9e9fb21SMatthew Dillon
1276d0fe8596SMatthew Dillon lwkt_exit();
1277d0fe8596SMatthew Dillon }
1278d0fe8596SMatthew Dillon
1279d0fe8596SMatthew Dillon static void
devfs_msg_exec(devfs_msg_t msg)1280d0fe8596SMatthew Dillon devfs_msg_exec(devfs_msg_t msg)
1281d0fe8596SMatthew Dillon {
1282d0fe8596SMatthew Dillon struct devfs_mnt_data *mnt;
1283d0fe8596SMatthew Dillon struct devfs_node *node;
1284d0fe8596SMatthew Dillon cdev_t dev;
1285bc185c5aSAlex Hornung
1286bc185c5aSAlex Hornung /*
1287bc185c5aSAlex Hornung * Acquire the devfs lock to ensure safety of all called functions
1288bc185c5aSAlex Hornung */
128921864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
129021864bc5SMatthew Dillon
1291d0fe8596SMatthew Dillon switch (msg->hdr.u.ms_result) {
129221864bc5SMatthew Dillon case DEVFS_DEVICE_CREATE:
1293ca8d7677SMatthew Dillon dev = msg->mdv_dev.dev;
1294ca8d7677SMatthew Dillon devfs_create_dev_worker(dev,
1295ca8d7677SMatthew Dillon msg->mdv_dev.uid,
1296ca8d7677SMatthew Dillon msg->mdv_dev.gid,
1297ca8d7677SMatthew Dillon msg->mdv_dev.perms);
129821864bc5SMatthew Dillon break;
129921864bc5SMatthew Dillon case DEVFS_DEVICE_DESTROY:
1300ca8d7677SMatthew Dillon dev = msg->mdv_dev.dev;
130121864bc5SMatthew Dillon devfs_destroy_dev_worker(dev);
130221864bc5SMatthew Dillon break;
130372ea429eSAlex Hornung case DEVFS_DESTROY_RELATED:
130472ea429eSAlex Hornung devfs_destroy_related_worker(msg->mdv_load);
130521864bc5SMatthew Dillon break;
130621864bc5SMatthew Dillon case DEVFS_DESTROY_DEV_BY_OPS:
1307ca8d7677SMatthew Dillon devfs_destroy_dev_by_ops_worker(msg->mdv_ops.ops,
1308ca8d7677SMatthew Dillon msg->mdv_ops.minor);
130921864bc5SMatthew Dillon break;
131021864bc5SMatthew Dillon case DEVFS_CREATE_ALL_DEV:
1311ca8d7677SMatthew Dillon node = (struct devfs_node *)msg->mdv_load;
131221864bc5SMatthew Dillon devfs_create_all_dev_worker(node);
131321864bc5SMatthew Dillon break;
131421864bc5SMatthew Dillon case DEVFS_MOUNT_ADD:
1315ca8d7677SMatthew Dillon mnt = msg->mdv_mnt;
131621864bc5SMatthew Dillon TAILQ_INSERT_TAIL(&devfs_mnt_list, mnt, link);
131721864bc5SMatthew Dillon devfs_create_all_dev_worker(mnt->root_node);
131821864bc5SMatthew Dillon break;
131921864bc5SMatthew Dillon case DEVFS_MOUNT_DEL:
1320ca8d7677SMatthew Dillon mnt = msg->mdv_mnt;
132121864bc5SMatthew Dillon TAILQ_REMOVE(&devfs_mnt_list, mnt, link);
132240c007d7SFrançois Tigeot /* Be sure to remove all the aliases first */
13231bef7707SMatthew Dillon devfs_iterate_topology(mnt->root_node,
13241bef7707SMatthew Dillon devfs_alias_reaper_callback,
132540c007d7SFrançois Tigeot NULL);
13261bef7707SMatthew Dillon devfs_iterate_topology(mnt->root_node,
13271bef7707SMatthew Dillon devfs_reaperp_callback,
132866abefa5SAlex Hornung NULL);
13291bef7707SMatthew Dillon devfs_iterate_orphans_unmount(mnt->mp);
13300709f681SMatthew Dillon if (mnt->leak_count) {
13310709f681SMatthew Dillon devfs_debug(DEVFS_DEBUG_SHOW,
133271f27d2dSMatthew Dillon "Leaked %ld devfs_node elements!\n",
13330709f681SMatthew Dillon mnt->leak_count);
13340709f681SMatthew Dillon }
133521864bc5SMatthew Dillon break;
133621864bc5SMatthew Dillon case DEVFS_CHANDLER_ADD:
1337bc185c5aSAlex Hornung devfs_chandler_add_worker(msg->mdv_chandler.name,
1338bc185c5aSAlex Hornung msg->mdv_chandler.nhandler);
133921864bc5SMatthew Dillon break;
134021864bc5SMatthew Dillon case DEVFS_CHANDLER_DEL:
1341ca8d7677SMatthew Dillon devfs_chandler_del_worker(msg->mdv_chandler.name);
134221864bc5SMatthew Dillon break;
134321864bc5SMatthew Dillon case DEVFS_FIND_DEVICE_BY_NAME:
134421864bc5SMatthew Dillon devfs_find_device_by_name_worker(msg);
134521864bc5SMatthew Dillon break;
13462ac7d105SSascha Wildner case DEVFS_FIND_DEVICE_BY_DEVID:
13472ac7d105SSascha Wildner devfs_find_device_by_devid_worker(msg);
134821864bc5SMatthew Dillon break;
134921864bc5SMatthew Dillon case DEVFS_MAKE_ALIAS:
1350ca8d7677SMatthew Dillon devfs_make_alias_worker((struct devfs_alias *)msg->mdv_load);
135121864bc5SMatthew Dillon break;
13528312ca30SAlex Hornung case DEVFS_DESTROY_ALIAS:
13538312ca30SAlex Hornung devfs_destroy_alias_worker((struct devfs_alias *)msg->mdv_load);
13548312ca30SAlex Hornung break;
135521864bc5SMatthew Dillon case DEVFS_APPLY_RULES:
1356ca8d7677SMatthew Dillon devfs_apply_reset_rules_caller(msg->mdv_name, 1);
135721864bc5SMatthew Dillon break;
135821864bc5SMatthew Dillon case DEVFS_RESET_RULES:
1359ca8d7677SMatthew Dillon devfs_apply_reset_rules_caller(msg->mdv_name, 0);
136021864bc5SMatthew Dillon break;
136121864bc5SMatthew Dillon case DEVFS_SCAN_CALLBACK:
13623a3826b3SAlex Hornung devfs_scan_callback_worker((devfs_scan_t *)msg->mdv_load,
13633a3826b3SAlex Hornung msg->mdv_load2);
1364ca8d7677SMatthew Dillon break;
136572ea429eSAlex Hornung case DEVFS_CLR_RELATED_FLAG:
136672ea429eSAlex Hornung devfs_clr_related_flag_worker(msg->mdv_flags.dev,
1367ca8d7677SMatthew Dillon msg->mdv_flags.flag);
1368ca8d7677SMatthew Dillon break;
136972ea429eSAlex Hornung case DEVFS_DESTROY_RELATED_WO_FLAG:
137072ea429eSAlex Hornung devfs_destroy_related_without_flag_worker(msg->mdv_flags.dev,
1371ca8d7677SMatthew Dillon msg->mdv_flags.flag);
137221864bc5SMatthew Dillon break;
1373fa7e6f37SAlex Hornung case DEVFS_INODE_TO_VNODE:
137466abefa5SAlex Hornung msg->mdv_ino.vp = devfs_iterate_topology(
1375fa7e6f37SAlex Hornung DEVFS_MNTDATA(msg->mdv_ino.mp)->root_node,
137666abefa5SAlex Hornung (devfs_iterate_callback_t *)devfs_inode_to_vnode_worker_callback,
137766abefa5SAlex Hornung &msg->mdv_ino.ino);
1378fa7e6f37SAlex Hornung break;
137921864bc5SMatthew Dillon case DEVFS_TERMINATE_CORE:
1380d0fe8596SMatthew Dillon devfs_run = 0;
138121864bc5SMatthew Dillon break;
138221864bc5SMatthew Dillon case DEVFS_SYNC:
138321864bc5SMatthew Dillon break;
138421864bc5SMatthew Dillon default:
1385bc185c5aSAlex Hornung devfs_debug(DEVFS_DEBUG_WARNING,
1386ca8d7677SMatthew Dillon "devfs_msg_core: unknown message "
1387ca8d7677SMatthew Dillon "received at core\n");
1388ca8d7677SMatthew Dillon break;
138921864bc5SMatthew Dillon }
139021864bc5SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
139121864bc5SMatthew Dillon }
139221864bc5SMatthew Dillon
139362c0a8e4SMichael Neumann static void
devfs_devctl_notify(cdev_t dev,const char * ev)1394249f9612SSascha Wildner devfs_devctl_notify(cdev_t dev, const char *ev)
139562c0a8e4SMichael Neumann {
139662c0a8e4SMichael Neumann static const char prefix[] = "cdev=";
139762c0a8e4SMichael Neumann char *data;
139862c0a8e4SMichael Neumann int namelen;
139962c0a8e4SMichael Neumann
140062c0a8e4SMichael Neumann namelen = strlen(dev->si_name);
140162c0a8e4SMichael Neumann data = kmalloc(namelen + sizeof(prefix), M_TEMP, M_WAITOK);
140262c0a8e4SMichael Neumann memcpy(data, prefix, sizeof(prefix) - 1);
140362c0a8e4SMichael Neumann memcpy(data + sizeof(prefix) - 1, dev->si_name, namelen + 1);
140462c0a8e4SMichael Neumann devctl_notify("DEVFS", "CDEV", ev, data);
140562c0a8e4SMichael Neumann kfree(data, M_TEMP);
140662c0a8e4SMichael Neumann }
140762c0a8e4SMichael Neumann
140821864bc5SMatthew Dillon /*
140921864bc5SMatthew Dillon * Worker function to insert a new dev into the dev list and initialize its
141021864bc5SMatthew Dillon * permissions. It also calls devfs_propagate_dev which in turn propagates
141121864bc5SMatthew Dillon * the change to all mount points.
1412ca8d7677SMatthew Dillon *
1413ca8d7677SMatthew Dillon * The passed dev is already referenced. This reference is eaten by this
1414ca8d7677SMatthew Dillon * function and represents the dev's linkage into devfs_dev_list.
141521864bc5SMatthew Dillon */
141621864bc5SMatthew Dillon static int
devfs_create_dev_worker(cdev_t dev,uid_t uid,gid_t gid,int perms)141721864bc5SMatthew Dillon devfs_create_dev_worker(cdev_t dev, uid_t uid, gid_t gid, int perms)
141821864bc5SMatthew Dillon {
141921864bc5SMatthew Dillon KKASSERT(dev);
142021864bc5SMatthew Dillon
142121864bc5SMatthew Dillon dev->si_uid = uid;
142221864bc5SMatthew Dillon dev->si_gid = gid;
142321864bc5SMatthew Dillon dev->si_perms = perms;
142421864bc5SMatthew Dillon
142521864bc5SMatthew Dillon devfs_link_dev(dev);
142621864bc5SMatthew Dillon devfs_propagate_dev(dev, 1);
142721864bc5SMatthew Dillon
14283a3826b3SAlex Hornung udev_event_attach(dev, NULL, 0);
1429249f9612SSascha Wildner devfs_devctl_notify(dev, "CREATE");
14303a3826b3SAlex Hornung
143121864bc5SMatthew Dillon return 0;
143221864bc5SMatthew Dillon }
143321864bc5SMatthew Dillon
143421864bc5SMatthew Dillon /*
143521864bc5SMatthew Dillon * Worker function to delete a dev from the dev list and free the cdev.
143621864bc5SMatthew Dillon * It also calls devfs_propagate_dev which in turn propagates the change
143721864bc5SMatthew Dillon * to all mount points.
143821864bc5SMatthew Dillon */
143921864bc5SMatthew Dillon static int
devfs_destroy_dev_worker(cdev_t dev)144021864bc5SMatthew Dillon devfs_destroy_dev_worker(cdev_t dev)
144121864bc5SMatthew Dillon {
1442ca8d7677SMatthew Dillon int error;
1443ca8d7677SMatthew Dillon
144421864bc5SMatthew Dillon KKASSERT(dev);
144521864bc5SMatthew Dillon KKASSERT((lockstatus(&devfs_lock, curthread)) == LK_EXCLUSIVE);
144621864bc5SMatthew Dillon
1447ca8d7677SMatthew Dillon error = devfs_unlink_dev(dev);
144821864bc5SMatthew Dillon devfs_propagate_dev(dev, 0);
14493a3826b3SAlex Hornung
1450249f9612SSascha Wildner devfs_devctl_notify(dev, "DESTROY");
14513a3826b3SAlex Hornung udev_event_detach(dev, NULL, 0);
14523a3826b3SAlex Hornung
1453ca8d7677SMatthew Dillon if (error == 0)
1454ca8d7677SMatthew Dillon release_dev(dev); /* link ref */
145521864bc5SMatthew Dillon release_dev(dev);
145621864bc5SMatthew Dillon release_dev(dev);
145721864bc5SMatthew Dillon
145821864bc5SMatthew Dillon return 0;
145921864bc5SMatthew Dillon }
146021864bc5SMatthew Dillon
146121864bc5SMatthew Dillon /*
146221864bc5SMatthew Dillon * Worker function to destroy all devices with a certain basename.
146321864bc5SMatthew Dillon * Calls devfs_destroy_dev_worker for the actual destruction.
146421864bc5SMatthew Dillon */
146521864bc5SMatthew Dillon static int
devfs_destroy_related_worker(cdev_t needle)146672ea429eSAlex Hornung devfs_destroy_related_worker(cdev_t needle)
146721864bc5SMatthew Dillon {
146872ea429eSAlex Hornung cdev_t dev;
146921864bc5SMatthew Dillon
147072ea429eSAlex Hornung restart:
147172ea429eSAlex Hornung devfs_debug(DEVFS_DEBUG_DEBUG, "related worker: %s\n",
147272ea429eSAlex Hornung needle->si_name);
147372ea429eSAlex Hornung TAILQ_FOREACH(dev, &devfs_dev_list, link) {
147472ea429eSAlex Hornung if (dev->si_parent == needle) {
147572ea429eSAlex Hornung devfs_destroy_related_worker(dev);
147621864bc5SMatthew Dillon devfs_destroy_dev_worker(dev);
147772ea429eSAlex Hornung goto restart;
1478ca8d7677SMatthew Dillon }
1479ca8d7677SMatthew Dillon }
1480ca8d7677SMatthew Dillon return 0;
1481ca8d7677SMatthew Dillon }
1482ca8d7677SMatthew Dillon
1483ca8d7677SMatthew Dillon static int
devfs_clr_related_flag_worker(cdev_t needle,uint32_t flag)148472ea429eSAlex Hornung devfs_clr_related_flag_worker(cdev_t needle, uint32_t flag)
1485ca8d7677SMatthew Dillon {
1486ca8d7677SMatthew Dillon cdev_t dev, dev1;
1487ca8d7677SMatthew Dillon
1488ca8d7677SMatthew Dillon TAILQ_FOREACH_MUTABLE(dev, &devfs_dev_list, link, dev1) {
148972ea429eSAlex Hornung if (dev->si_parent == needle) {
149072ea429eSAlex Hornung devfs_clr_related_flag_worker(dev, flag);
1491ca8d7677SMatthew Dillon dev->si_flags &= ~flag;
1492ca8d7677SMatthew Dillon }
1493ca8d7677SMatthew Dillon }
1494ca8d7677SMatthew Dillon
1495ca8d7677SMatthew Dillon return 0;
1496ca8d7677SMatthew Dillon }
1497ca8d7677SMatthew Dillon
1498ca8d7677SMatthew Dillon static int
devfs_destroy_related_without_flag_worker(cdev_t needle,uint32_t flag)149972ea429eSAlex Hornung devfs_destroy_related_without_flag_worker(cdev_t needle, uint32_t flag)
1500ca8d7677SMatthew Dillon {
150172ea429eSAlex Hornung cdev_t dev;
1502ca8d7677SMatthew Dillon
150372ea429eSAlex Hornung restart:
150472ea429eSAlex Hornung devfs_debug(DEVFS_DEBUG_DEBUG, "related_wo_flag: %s\n",
150572ea429eSAlex Hornung needle->si_name);
150672ea429eSAlex Hornung
150772ea429eSAlex Hornung TAILQ_FOREACH(dev, &devfs_dev_list, link) {
150872ea429eSAlex Hornung if (dev->si_parent == needle) {
150972ea429eSAlex Hornung devfs_destroy_related_without_flag_worker(dev, flag);
1510ca8d7677SMatthew Dillon if (!(dev->si_flags & flag)) {
1511ca8d7677SMatthew Dillon devfs_destroy_dev_worker(dev);
151272ea429eSAlex Hornung devfs_debug(DEVFS_DEBUG_DEBUG,
151372ea429eSAlex Hornung "related_wo_flag: %s restart\n", dev->si_name);
151472ea429eSAlex Hornung goto restart;
1515ca8d7677SMatthew Dillon }
1516ca8d7677SMatthew Dillon }
151721864bc5SMatthew Dillon }
151821864bc5SMatthew Dillon
151921864bc5SMatthew Dillon return 0;
152021864bc5SMatthew Dillon }
152121864bc5SMatthew Dillon
152221864bc5SMatthew Dillon /*
152321864bc5SMatthew Dillon * Worker function that creates all device nodes on top of a devfs
152421864bc5SMatthew Dillon * root node.
152521864bc5SMatthew Dillon */
152621864bc5SMatthew Dillon static int
devfs_create_all_dev_worker(struct devfs_node * root)152721864bc5SMatthew Dillon devfs_create_all_dev_worker(struct devfs_node *root)
152821864bc5SMatthew Dillon {
152921864bc5SMatthew Dillon cdev_t dev;
153021864bc5SMatthew Dillon
153121864bc5SMatthew Dillon KKASSERT(root);
153221864bc5SMatthew Dillon
153321864bc5SMatthew Dillon TAILQ_FOREACH(dev, &devfs_dev_list, link) {
15342c94b9eeSMatthew Dillon devfs_create_device_node(root, dev, NULL, NULL, NULL);
153521864bc5SMatthew Dillon }
1536bc185c5aSAlex Hornung
153721864bc5SMatthew Dillon return 0;
153821864bc5SMatthew Dillon }
153921864bc5SMatthew Dillon
154021864bc5SMatthew Dillon /*
154121864bc5SMatthew Dillon * Worker function that destroys all devices that match a specific
154221864bc5SMatthew Dillon * dev_ops and/or minor. If minor is less than 0, it is not matched
154321864bc5SMatthew Dillon * against. It also propagates all changes.
154421864bc5SMatthew Dillon */
154521864bc5SMatthew Dillon static int
devfs_destroy_dev_by_ops_worker(struct dev_ops * ops,int minor)154621864bc5SMatthew Dillon devfs_destroy_dev_by_ops_worker(struct dev_ops *ops, int minor)
154721864bc5SMatthew Dillon {
154821864bc5SMatthew Dillon cdev_t dev, dev1;
154921864bc5SMatthew Dillon
155021864bc5SMatthew Dillon KKASSERT(ops);
155121864bc5SMatthew Dillon
155221864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(dev, &devfs_dev_list, link, dev1) {
1553ca8d7677SMatthew Dillon if (dev->si_ops != ops)
1554ca8d7677SMatthew Dillon continue;
155521864bc5SMatthew Dillon if ((minor < 0) || (dev->si_uminor == minor)) {
1556ca8d7677SMatthew Dillon devfs_destroy_dev_worker(dev);
155721864bc5SMatthew Dillon }
155821864bc5SMatthew Dillon }
1559bc185c5aSAlex Hornung
156021864bc5SMatthew Dillon return 0;
156121864bc5SMatthew Dillon }
156221864bc5SMatthew Dillon
156321864bc5SMatthew Dillon /*
156421864bc5SMatthew Dillon * Worker function that registers a new clone handler in devfs.
156521864bc5SMatthew Dillon */
156621864bc5SMatthew Dillon static int
devfs_chandler_add_worker(const char * name,d_clone_t * nhandler)156707dfa375SAlex Hornung devfs_chandler_add_worker(const char *name, d_clone_t *nhandler)
156821864bc5SMatthew Dillon {
156921864bc5SMatthew Dillon struct devfs_clone_handler *chandler = NULL;
157021864bc5SMatthew Dillon u_char len = strlen(name);
157121864bc5SMatthew Dillon
1572ca8d7677SMatthew Dillon if (len == 0)
157321864bc5SMatthew Dillon return 1;
157421864bc5SMatthew Dillon
157521864bc5SMatthew Dillon TAILQ_FOREACH(chandler, &devfs_chandler_list, link) {
1576bc185c5aSAlex Hornung if (chandler->namlen != len)
1577bc185c5aSAlex Hornung continue;
1578bc185c5aSAlex Hornung
157921864bc5SMatthew Dillon if (!memcmp(chandler->name, name, len)) {
158021864bc5SMatthew Dillon /* Clonable basename already exists */
158121864bc5SMatthew Dillon return 1;
158221864bc5SMatthew Dillon }
158321864bc5SMatthew Dillon }
158421864bc5SMatthew Dillon
1585ca8d7677SMatthew Dillon chandler = kmalloc(sizeof(*chandler), M_DEVFS, M_WAITOK | M_ZERO);
158607dfa375SAlex Hornung chandler->name = kstrdup(name, M_DEVFS);
158721864bc5SMatthew Dillon chandler->namlen = len;
158821864bc5SMatthew Dillon chandler->nhandler = nhandler;
158921864bc5SMatthew Dillon
159021864bc5SMatthew Dillon TAILQ_INSERT_TAIL(&devfs_chandler_list, chandler, link);
159121864bc5SMatthew Dillon return 0;
159221864bc5SMatthew Dillon }
159321864bc5SMatthew Dillon
159421864bc5SMatthew Dillon /*
159521864bc5SMatthew Dillon * Worker function that removes a given clone handler from the
159621864bc5SMatthew Dillon * clone handler list.
159721864bc5SMatthew Dillon */
159821864bc5SMatthew Dillon static int
devfs_chandler_del_worker(const char * name)159907dfa375SAlex Hornung devfs_chandler_del_worker(const char *name)
160021864bc5SMatthew Dillon {
160121864bc5SMatthew Dillon struct devfs_clone_handler *chandler, *chandler2;
160221864bc5SMatthew Dillon u_char len = strlen(name);
160321864bc5SMatthew Dillon
1604ca8d7677SMatthew Dillon if (len == 0)
160521864bc5SMatthew Dillon return 1;
160621864bc5SMatthew Dillon
160721864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(chandler, &devfs_chandler_list, link, chandler2) {
1608ca8d7677SMatthew Dillon if (chandler->namlen != len)
1609ca8d7677SMatthew Dillon continue;
1610ca8d7677SMatthew Dillon if (memcmp(chandler->name, name, len))
1611ca8d7677SMatthew Dillon continue;
1612bc185c5aSAlex Hornung
161321864bc5SMatthew Dillon TAILQ_REMOVE(&devfs_chandler_list, chandler, link);
16145298e788SAlex Hornung kfree(chandler->name, M_DEVFS);
161521864bc5SMatthew Dillon kfree(chandler, M_DEVFS);
16165298e788SAlex Hornung break;
161721864bc5SMatthew Dillon }
161821864bc5SMatthew Dillon
161921864bc5SMatthew Dillon return 0;
162021864bc5SMatthew Dillon }
162121864bc5SMatthew Dillon
162221864bc5SMatthew Dillon /*
162321864bc5SMatthew Dillon * Worker function that finds a given device name and changes
162421864bc5SMatthew Dillon * the message received accordingly so that when replied to,
162521864bc5SMatthew Dillon * the answer is returned to the caller.
162621864bc5SMatthew Dillon */
162721864bc5SMatthew Dillon static int
devfs_find_device_by_name_worker(devfs_msg_t devfs_msg)162821864bc5SMatthew Dillon devfs_find_device_by_name_worker(devfs_msg_t devfs_msg)
162921864bc5SMatthew Dillon {
16306507240bSMatthew Dillon struct devfs_alias *alias;
16316507240bSMatthew Dillon cdev_t dev;
163221864bc5SMatthew Dillon cdev_t found = NULL;
163321864bc5SMatthew Dillon
16346507240bSMatthew Dillon TAILQ_FOREACH(dev, &devfs_dev_list, link) {
16356507240bSMatthew Dillon if (strcmp(devfs_msg->mdv_name, dev->si_name) == 0) {
163621864bc5SMatthew Dillon found = dev;
163721864bc5SMatthew Dillon break;
163821864bc5SMatthew Dillon }
163921864bc5SMatthew Dillon }
16406507240bSMatthew Dillon if (found == NULL) {
16416507240bSMatthew Dillon TAILQ_FOREACH(alias, &devfs_alias_list, link) {
16426507240bSMatthew Dillon if (strcmp(devfs_msg->mdv_name, alias->name) == 0) {
16436507240bSMatthew Dillon found = alias->dev_target;
16446507240bSMatthew Dillon break;
16456507240bSMatthew Dillon }
16466507240bSMatthew Dillon }
16476507240bSMatthew Dillon }
1648ca8d7677SMatthew Dillon devfs_msg->mdv_cdev = found;
164921864bc5SMatthew Dillon
165021864bc5SMatthew Dillon return 0;
165121864bc5SMatthew Dillon }
165221864bc5SMatthew Dillon
165321864bc5SMatthew Dillon /*
165421864bc5SMatthew Dillon * Worker function that finds a given device udev and changes
165521864bc5SMatthew Dillon * the message received accordingly so that when replied to,
165621864bc5SMatthew Dillon * the answer is returned to the caller.
165721864bc5SMatthew Dillon */
165821864bc5SMatthew Dillon static int
devfs_find_device_by_devid_worker(devfs_msg_t devfs_msg)16592ac7d105SSascha Wildner devfs_find_device_by_devid_worker(devfs_msg_t devfs_msg)
166021864bc5SMatthew Dillon {
166121864bc5SMatthew Dillon cdev_t dev, dev1;
166221864bc5SMatthew Dillon cdev_t found = NULL;
166321864bc5SMatthew Dillon
166421864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(dev, &devfs_dev_list, link, dev1) {
166591ffdfc5SSascha Wildner if (((dev_t)dev->si_inode) == devfs_msg->mdv_udev) {
166621864bc5SMatthew Dillon found = dev;
166721864bc5SMatthew Dillon break;
166821864bc5SMatthew Dillon }
166921864bc5SMatthew Dillon }
1670ca8d7677SMatthew Dillon devfs_msg->mdv_cdev = found;
167121864bc5SMatthew Dillon
167221864bc5SMatthew Dillon return 0;
167321864bc5SMatthew Dillon }
167421864bc5SMatthew Dillon
167521864bc5SMatthew Dillon /*
167621864bc5SMatthew Dillon * Worker function that inserts a given alias into the
167721864bc5SMatthew Dillon * alias list, and propagates the alias to all mount
167821864bc5SMatthew Dillon * points.
167921864bc5SMatthew Dillon */
168021864bc5SMatthew Dillon static int
devfs_make_alias_worker(struct devfs_alias * alias)168121864bc5SMatthew Dillon devfs_make_alias_worker(struct devfs_alias *alias)
168221864bc5SMatthew Dillon {
168321864bc5SMatthew Dillon struct devfs_alias *alias2;
168421864bc5SMatthew Dillon size_t len = strlen(alias->name);
168521864bc5SMatthew Dillon int found = 0;
168621864bc5SMatthew Dillon
168721864bc5SMatthew Dillon TAILQ_FOREACH(alias2, &devfs_alias_list, link) {
1688bc185c5aSAlex Hornung if (len != alias2->namlen)
1689bc185c5aSAlex Hornung continue;
1690bc185c5aSAlex Hornung
16915298e788SAlex Hornung if (!memcmp(alias->name, alias2->name, len)) {
169221864bc5SMatthew Dillon found = 1;
169321864bc5SMatthew Dillon break;
169421864bc5SMatthew Dillon }
169521864bc5SMatthew Dillon }
169621864bc5SMatthew Dillon
169721864bc5SMatthew Dillon if (!found) {
1698bc185c5aSAlex Hornung /*
1699bc185c5aSAlex Hornung * The alias doesn't exist yet, so we add it to the alias list
1700bc185c5aSAlex Hornung */
170121864bc5SMatthew Dillon TAILQ_INSERT_TAIL(&devfs_alias_list, alias, link);
17028312ca30SAlex Hornung devfs_alias_propagate(alias, 0);
17033a3826b3SAlex Hornung udev_event_attach(alias->dev_target, alias->name, 1);
170421864bc5SMatthew Dillon } else {
17055298e788SAlex Hornung devfs_debug(DEVFS_DEBUG_WARNING,
1706ca8d7677SMatthew Dillon "Warning: duplicate devfs_make_alias for %s\n",
1707ca8d7677SMatthew Dillon alias->name);
17085298e788SAlex Hornung kfree(alias->name, M_DEVFS);
170921864bc5SMatthew Dillon kfree(alias, M_DEVFS);
171021864bc5SMatthew Dillon }
171121864bc5SMatthew Dillon
171221864bc5SMatthew Dillon return 0;
171321864bc5SMatthew Dillon }
171421864bc5SMatthew Dillon
171521864bc5SMatthew Dillon /*
17168312ca30SAlex Hornung * Worker function that delete a given alias from the
17178312ca30SAlex Hornung * alias list, and propagates the removal to all mount
17188312ca30SAlex Hornung * points.
17198312ca30SAlex Hornung */
17208312ca30SAlex Hornung static int
devfs_destroy_alias_worker(struct devfs_alias * alias)17218312ca30SAlex Hornung devfs_destroy_alias_worker(struct devfs_alias *alias)
17228312ca30SAlex Hornung {
17238312ca30SAlex Hornung struct devfs_alias *alias2;
17248312ca30SAlex Hornung int found = 0;
17258312ca30SAlex Hornung
17268312ca30SAlex Hornung TAILQ_FOREACH(alias2, &devfs_alias_list, link) {
17278312ca30SAlex Hornung if (alias->dev_target != alias2->dev_target)
17288312ca30SAlex Hornung continue;
17298312ca30SAlex Hornung
17308312ca30SAlex Hornung if (devfs_WildCmp(alias->name, alias2->name) == 0) {
17318312ca30SAlex Hornung found = 1;
17328312ca30SAlex Hornung break;
17338312ca30SAlex Hornung }
17348312ca30SAlex Hornung }
17358312ca30SAlex Hornung
17368312ca30SAlex Hornung if (!found) {
17378312ca30SAlex Hornung devfs_debug(DEVFS_DEBUG_WARNING,
17388312ca30SAlex Hornung "Warning: devfs_destroy_alias for inexistant alias: %s\n",
17398312ca30SAlex Hornung alias->name);
17408312ca30SAlex Hornung kfree(alias->name, M_DEVFS);
17418312ca30SAlex Hornung kfree(alias, M_DEVFS);
17428312ca30SAlex Hornung } else {
17438312ca30SAlex Hornung /*
17448312ca30SAlex Hornung * The alias exists, so we delete it from the alias list
17458312ca30SAlex Hornung */
17468312ca30SAlex Hornung TAILQ_REMOVE(&devfs_alias_list, alias2, link);
17478312ca30SAlex Hornung devfs_alias_propagate(alias2, 1);
17488312ca30SAlex Hornung udev_event_detach(alias2->dev_target, alias2->name, 1);
17498312ca30SAlex Hornung kfree(alias->name, M_DEVFS);
17508312ca30SAlex Hornung kfree(alias, M_DEVFS);
17518312ca30SAlex Hornung kfree(alias2->name, M_DEVFS);
17528312ca30SAlex Hornung kfree(alias2, M_DEVFS);
17538312ca30SAlex Hornung }
17548312ca30SAlex Hornung
17558312ca30SAlex Hornung return 0;
17568312ca30SAlex Hornung }
17578312ca30SAlex Hornung
17588312ca30SAlex Hornung /*
175921864bc5SMatthew Dillon * Function that removes and frees all aliases.
176021864bc5SMatthew Dillon */
176121864bc5SMatthew Dillon static int
devfs_alias_reap(void)176221864bc5SMatthew Dillon devfs_alias_reap(void)
176321864bc5SMatthew Dillon {
176421864bc5SMatthew Dillon struct devfs_alias *alias, *alias2;
176521864bc5SMatthew Dillon
176621864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(alias, &devfs_alias_list, link, alias2) {
176721864bc5SMatthew Dillon TAILQ_REMOVE(&devfs_alias_list, alias, link);
17688312ca30SAlex Hornung kfree(alias->name, M_DEVFS);
176921864bc5SMatthew Dillon kfree(alias, M_DEVFS);
177021864bc5SMatthew Dillon }
177121864bc5SMatthew Dillon return 0;
177221864bc5SMatthew Dillon }
177321864bc5SMatthew Dillon
177421864bc5SMatthew Dillon /*
177521864bc5SMatthew Dillon * Function that removes an alias matching a specific cdev and frees
177621864bc5SMatthew Dillon * it accordingly.
177721864bc5SMatthew Dillon */
177821864bc5SMatthew Dillon static int
devfs_alias_remove(cdev_t dev)177921864bc5SMatthew Dillon devfs_alias_remove(cdev_t dev)
178021864bc5SMatthew Dillon {
178121864bc5SMatthew Dillon struct devfs_alias *alias, *alias2;
178221864bc5SMatthew Dillon
178321864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(alias, &devfs_alias_list, link, alias2) {
178421864bc5SMatthew Dillon if (alias->dev_target == dev) {
178521864bc5SMatthew Dillon TAILQ_REMOVE(&devfs_alias_list, alias, link);
17863a3826b3SAlex Hornung udev_event_detach(alias->dev_target, alias->name, 1);
17878312ca30SAlex Hornung kfree(alias->name, M_DEVFS);
178821864bc5SMatthew Dillon kfree(alias, M_DEVFS);
178921864bc5SMatthew Dillon }
179021864bc5SMatthew Dillon }
179121864bc5SMatthew Dillon return 0;
179221864bc5SMatthew Dillon }
179321864bc5SMatthew Dillon
179421864bc5SMatthew Dillon /*
17958312ca30SAlex Hornung * This function propagates an alias addition or removal to
17968312ca30SAlex Hornung * all mount points.
179721864bc5SMatthew Dillon */
179821864bc5SMatthew Dillon static int
devfs_alias_propagate(struct devfs_alias * alias,int remove)17998312ca30SAlex Hornung devfs_alias_propagate(struct devfs_alias *alias, int remove)
180021864bc5SMatthew Dillon {
180121864bc5SMatthew Dillon struct devfs_mnt_data *mnt;
180221864bc5SMatthew Dillon
180321864bc5SMatthew Dillon TAILQ_FOREACH(mnt, &devfs_mnt_list, link) {
18048312ca30SAlex Hornung if (remove) {
18058312ca30SAlex Hornung devfs_destroy_node(mnt->root_node, alias->name);
18068312ca30SAlex Hornung } else {
180721864bc5SMatthew Dillon devfs_alias_apply(mnt->root_node, alias);
180821864bc5SMatthew Dillon }
18098312ca30SAlex Hornung }
181021864bc5SMatthew Dillon return 0;
181121864bc5SMatthew Dillon }
181221864bc5SMatthew Dillon
181321864bc5SMatthew Dillon /*
181421864bc5SMatthew Dillon * This function is a recursive function iterating through
181521864bc5SMatthew Dillon * all device nodes in the topology and, if applicable,
181621864bc5SMatthew Dillon * creating the relevant alias for a device node.
181721864bc5SMatthew Dillon */
181821864bc5SMatthew Dillon static int
devfs_alias_apply(struct devfs_node * node,struct devfs_alias * alias)181921864bc5SMatthew Dillon devfs_alias_apply(struct devfs_node *node, struct devfs_alias *alias)
182021864bc5SMatthew Dillon {
182121864bc5SMatthew Dillon struct devfs_node *node1, *node2;
182221864bc5SMatthew Dillon
182321864bc5SMatthew Dillon KKASSERT(alias != NULL);
182421864bc5SMatthew Dillon
18258e78a293SSascha Wildner if ((node->node_type == Nroot) || (node->node_type == Ndir)) {
182621864bc5SMatthew Dillon if (node->nchildren > 2) {
182721864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(node1, DEVFS_DENODE_HEAD(node), link, node2) {
182821864bc5SMatthew Dillon devfs_alias_apply(node1, alias);
182921864bc5SMatthew Dillon }
183021864bc5SMatthew Dillon }
183121864bc5SMatthew Dillon } else {
183221864bc5SMatthew Dillon if (node->d_dev == alias->dev_target)
18331cb12919SAlex Hornung devfs_alias_create(alias->name, node, 0);
183421864bc5SMatthew Dillon }
183521864bc5SMatthew Dillon return 0;
183621864bc5SMatthew Dillon }
183721864bc5SMatthew Dillon
183821864bc5SMatthew Dillon /*
183921864bc5SMatthew Dillon * This function checks if any alias possibly is applicable
184021864bc5SMatthew Dillon * to the given node. If so, the alias is created.
184121864bc5SMatthew Dillon */
184221864bc5SMatthew Dillon static int
devfs_alias_check_create(struct devfs_node * node)184321864bc5SMatthew Dillon devfs_alias_check_create(struct devfs_node *node)
184421864bc5SMatthew Dillon {
184521864bc5SMatthew Dillon struct devfs_alias *alias;
184621864bc5SMatthew Dillon
184721864bc5SMatthew Dillon TAILQ_FOREACH(alias, &devfs_alias_list, link) {
184821864bc5SMatthew Dillon if (node->d_dev == alias->dev_target)
18491cb12919SAlex Hornung devfs_alias_create(alias->name, node, 0);
185021864bc5SMatthew Dillon }
185121864bc5SMatthew Dillon return 0;
185221864bc5SMatthew Dillon }
185321864bc5SMatthew Dillon
185421864bc5SMatthew Dillon /*
185521864bc5SMatthew Dillon * This function creates an alias with a given name
185621864bc5SMatthew Dillon * linking to a given devfs node. It also increments
185721864bc5SMatthew Dillon * the link count on the target node.
185821864bc5SMatthew Dillon */
185921864bc5SMatthew Dillon int
devfs_alias_create(char * name_orig,struct devfs_node * target,int rule_based)18601cb12919SAlex Hornung devfs_alias_create(char *name_orig, struct devfs_node *target, int rule_based)
186121864bc5SMatthew Dillon {
186221864bc5SMatthew Dillon struct mount *mp = target->mp;
186321864bc5SMatthew Dillon struct devfs_node *parent = DEVFS_MNTDATA(mp)->root_node;
186421864bc5SMatthew Dillon struct devfs_node *linknode;
186521864bc5SMatthew Dillon char *create_path = NULL;
1866da655383SMatthew Dillon char *name;
1867da655383SMatthew Dillon char *name_buf;
1868da655383SMatthew Dillon int result = 0;
186921864bc5SMatthew Dillon
187021864bc5SMatthew Dillon KKASSERT((lockstatus(&devfs_lock, curthread)) == LK_EXCLUSIVE);
187121864bc5SMatthew Dillon
1872da655383SMatthew Dillon name_buf = kmalloc(PATH_MAX, M_TEMP, M_WAITOK);
187321864bc5SMatthew Dillon devfs_resolve_name_path(name_orig, name_buf, &create_path, &name);
187421864bc5SMatthew Dillon
187521864bc5SMatthew Dillon if (create_path)
187621864bc5SMatthew Dillon parent = devfs_resolve_or_create_path(parent, create_path, 1);
187721864bc5SMatthew Dillon
187821864bc5SMatthew Dillon
187921864bc5SMatthew Dillon if (devfs_find_device_node_by_name(parent, name)) {
1880bc185c5aSAlex Hornung devfs_debug(DEVFS_DEBUG_WARNING,
1881ca8d7677SMatthew Dillon "Node already exists: %s "
1882ca8d7677SMatthew Dillon "(devfs_make_alias_worker)!\n",
1883ca8d7677SMatthew Dillon name);
1884da655383SMatthew Dillon result = 1;
1885da655383SMatthew Dillon goto done;
188621864bc5SMatthew Dillon }
188721864bc5SMatthew Dillon
18888e78a293SSascha Wildner linknode = devfs_allocp(Nlink, name, parent, mp, NULL);
1889da655383SMatthew Dillon if (linknode == NULL) {
1890da655383SMatthew Dillon result = 1;
1891da655383SMatthew Dillon goto done;
1892da655383SMatthew Dillon }
189321864bc5SMatthew Dillon
189421864bc5SMatthew Dillon linknode->link_target = target;
189521864bc5SMatthew Dillon target->nlinks++;
189621864bc5SMatthew Dillon
18971cb12919SAlex Hornung if (rule_based)
18981cb12919SAlex Hornung linknode->flags |= DEVFS_RULE_CREATED;
18991cb12919SAlex Hornung
1900da655383SMatthew Dillon done:
1901da655383SMatthew Dillon kfree(name_buf, M_TEMP);
1902da655383SMatthew Dillon return (result);
190321864bc5SMatthew Dillon }
190421864bc5SMatthew Dillon
190521864bc5SMatthew Dillon /*
190621864bc5SMatthew Dillon * This function is called by the core and handles mount point
190721864bc5SMatthew Dillon * strings. It either calls the relevant worker (devfs_apply_
190821864bc5SMatthew Dillon * reset_rules_worker) on all mountpoints or only a specific
190921864bc5SMatthew Dillon * one.
191021864bc5SMatthew Dillon */
191121864bc5SMatthew Dillon static int
devfs_apply_reset_rules_caller(char * mountto,int apply)191221864bc5SMatthew Dillon devfs_apply_reset_rules_caller(char *mountto, int apply)
191321864bc5SMatthew Dillon {
191421864bc5SMatthew Dillon struct devfs_mnt_data *mnt;
191521864bc5SMatthew Dillon
1916bc185c5aSAlex Hornung if (mountto[0] == '*') {
191721864bc5SMatthew Dillon TAILQ_FOREACH(mnt, &devfs_mnt_list, link) {
191866abefa5SAlex Hornung devfs_iterate_topology(mnt->root_node,
191966abefa5SAlex Hornung (apply)?(devfs_rule_check_apply):(devfs_rule_reset_node),
192066abefa5SAlex Hornung NULL);
192121864bc5SMatthew Dillon }
192221864bc5SMatthew Dillon } else {
192321864bc5SMatthew Dillon TAILQ_FOREACH(mnt, &devfs_mnt_list, link) {
19249cf39e57SAlex Hornung if (!strcmp(mnt->mp->mnt_stat.f_mntonname, mountto)) {
192566abefa5SAlex Hornung devfs_iterate_topology(mnt->root_node,
192666abefa5SAlex Hornung (apply)?(devfs_rule_check_apply):(devfs_rule_reset_node),
192766abefa5SAlex Hornung NULL);
1928bc185c5aSAlex Hornung break;
1929bc185c5aSAlex Hornung }
193021864bc5SMatthew Dillon }
193121864bc5SMatthew Dillon }
193221864bc5SMatthew Dillon
193321864bc5SMatthew Dillon kfree(mountto, M_DEVFS);
193421864bc5SMatthew Dillon return 0;
193521864bc5SMatthew Dillon }
193621864bc5SMatthew Dillon
193721864bc5SMatthew Dillon /*
193821864bc5SMatthew Dillon * This function calls a given callback function for
193921864bc5SMatthew Dillon * every dev node in the devfs dev list.
194021864bc5SMatthew Dillon */
194121864bc5SMatthew Dillon static int
devfs_scan_callback_worker(devfs_scan_t * callback,void * arg)19423a3826b3SAlex Hornung devfs_scan_callback_worker(devfs_scan_t *callback, void *arg)
194321864bc5SMatthew Dillon {
194421864bc5SMatthew Dillon cdev_t dev, dev1;
1945b28e21efSSascha Wildner struct devfs_alias *alias, *alias1;
194621864bc5SMatthew Dillon
194721864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(dev, &devfs_dev_list, link, dev1) {
1948b28e21efSSascha Wildner callback(dev->si_name, dev, false, arg);
1949b28e21efSSascha Wildner }
1950b28e21efSSascha Wildner TAILQ_FOREACH_MUTABLE(alias, &devfs_alias_list, link, alias1) {
1951b28e21efSSascha Wildner callback(alias->name, alias->dev_target, true, arg);
195221864bc5SMatthew Dillon }
195321864bc5SMatthew Dillon
195421864bc5SMatthew Dillon return 0;
195521864bc5SMatthew Dillon }
195621864bc5SMatthew Dillon
195721864bc5SMatthew Dillon /*
195821864bc5SMatthew Dillon * This function tries to resolve a given directory, or if not
195921864bc5SMatthew Dillon * found and creation requested, creates the given directory.
196021864bc5SMatthew Dillon */
196121864bc5SMatthew Dillon static struct devfs_node *
devfs_resolve_or_create_dir(struct devfs_node * parent,char * dir_name,size_t name_len,int create)1962ca8d7677SMatthew Dillon devfs_resolve_or_create_dir(struct devfs_node *parent, char *dir_name,
1963ca8d7677SMatthew Dillon size_t name_len, int create)
196421864bc5SMatthew Dillon {
196521864bc5SMatthew Dillon struct devfs_node *node, *found = NULL;
196621864bc5SMatthew Dillon
196721864bc5SMatthew Dillon TAILQ_FOREACH(node, DEVFS_DENODE_HEAD(parent), link) {
1968bc185c5aSAlex Hornung if (name_len != node->d_dir.d_namlen)
1969bc185c5aSAlex Hornung continue;
1970bc185c5aSAlex Hornung
197121864bc5SMatthew Dillon if (!memcmp(dir_name, node->d_dir.d_name, name_len)) {
197221864bc5SMatthew Dillon found = node;
197321864bc5SMatthew Dillon break;
197421864bc5SMatthew Dillon }
197521864bc5SMatthew Dillon }
197621864bc5SMatthew Dillon
197721864bc5SMatthew Dillon if ((found == NULL) && (create)) {
19788e78a293SSascha Wildner found = devfs_allocp(Ndir, dir_name, parent, parent->mp, NULL);
197921864bc5SMatthew Dillon }
198021864bc5SMatthew Dillon
198121864bc5SMatthew Dillon return found;
198221864bc5SMatthew Dillon }
198321864bc5SMatthew Dillon
198421864bc5SMatthew Dillon /*
198521864bc5SMatthew Dillon * This function tries to resolve a complete path. If creation is requested,
198621864bc5SMatthew Dillon * if a given part of the path cannot be resolved (because it doesn't exist),
198721864bc5SMatthew Dillon * it is created.
198821864bc5SMatthew Dillon */
198921864bc5SMatthew Dillon struct devfs_node *
devfs_resolve_or_create_path(struct devfs_node * parent,char * path,int create)199021864bc5SMatthew Dillon devfs_resolve_or_create_path(struct devfs_node *parent, char *path, int create)
199121864bc5SMatthew Dillon {
199221864bc5SMatthew Dillon struct devfs_node *node = parent;
1993da655383SMatthew Dillon char *buf;
199421864bc5SMatthew Dillon size_t idx = 0;
199521864bc5SMatthew Dillon
199621864bc5SMatthew Dillon if (path == NULL)
199721864bc5SMatthew Dillon return parent;
199821864bc5SMatthew Dillon
1999da655383SMatthew Dillon buf = kmalloc(PATH_MAX, M_TEMP, M_WAITOK);
200021864bc5SMatthew Dillon
2001da655383SMatthew Dillon while (*path && idx < PATH_MAX - 1) {
200221864bc5SMatthew Dillon if (*path != '/') {
200321864bc5SMatthew Dillon buf[idx++] = *path;
200421864bc5SMatthew Dillon } else {
200521864bc5SMatthew Dillon buf[idx] = '\0';
200621864bc5SMatthew Dillon node = devfs_resolve_or_create_dir(node, buf, idx, create);
2007da655383SMatthew Dillon if (node == NULL) {
2008da655383SMatthew Dillon kfree(buf, M_TEMP);
200921864bc5SMatthew Dillon return NULL;
2010da655383SMatthew Dillon }
201121864bc5SMatthew Dillon idx = 0;
201221864bc5SMatthew Dillon }
2013da655383SMatthew Dillon ++path;
201421864bc5SMatthew Dillon }
201521864bc5SMatthew Dillon buf[idx] = '\0';
2016da655383SMatthew Dillon node = devfs_resolve_or_create_dir(node, buf, idx, create);
2017da655383SMatthew Dillon kfree (buf, M_TEMP);
2018da655383SMatthew Dillon return (node);
201921864bc5SMatthew Dillon }
202021864bc5SMatthew Dillon
202121864bc5SMatthew Dillon /*
202221864bc5SMatthew Dillon * Takes a full path and strips it into a directory path and a name.
202321864bc5SMatthew Dillon * For a/b/c/foo, it returns foo in namep and a/b/c in pathp. It
202421864bc5SMatthew Dillon * requires a working buffer with enough size to keep the whole
202521864bc5SMatthew Dillon * fullpath.
202621864bc5SMatthew Dillon */
202721864bc5SMatthew Dillon int
devfs_resolve_name_path(char * fullpath,char * buf,char ** pathp,char ** namep)202821864bc5SMatthew Dillon devfs_resolve_name_path(char *fullpath, char *buf, char **pathp, char **namep)
202921864bc5SMatthew Dillon {
203021864bc5SMatthew Dillon char *name = NULL;
203121864bc5SMatthew Dillon char *path = NULL;
203221864bc5SMatthew Dillon size_t len = strlen(fullpath) + 1;
203321864bc5SMatthew Dillon int i;
203421864bc5SMatthew Dillon
2035bc185c5aSAlex Hornung KKASSERT((fullpath != NULL) && (buf != NULL));
2036bc185c5aSAlex Hornung KKASSERT((pathp != NULL) && (namep != NULL));
203721864bc5SMatthew Dillon
203821864bc5SMatthew Dillon memcpy(buf, fullpath, len);
203921864bc5SMatthew Dillon
204021864bc5SMatthew Dillon for (i = len-1; i>= 0; i--) {
204121864bc5SMatthew Dillon if (buf[i] == '/') {
204221864bc5SMatthew Dillon buf[i] = '\0';
204321864bc5SMatthew Dillon name = &(buf[i+1]);
204421864bc5SMatthew Dillon path = buf;
204521864bc5SMatthew Dillon break;
204621864bc5SMatthew Dillon }
204721864bc5SMatthew Dillon }
204821864bc5SMatthew Dillon
204921864bc5SMatthew Dillon *pathp = path;
205021864bc5SMatthew Dillon
205121864bc5SMatthew Dillon if (name) {
205221864bc5SMatthew Dillon *namep = name;
205321864bc5SMatthew Dillon } else {
205421864bc5SMatthew Dillon *namep = buf;
205521864bc5SMatthew Dillon }
205621864bc5SMatthew Dillon
205721864bc5SMatthew Dillon return 0;
205821864bc5SMatthew Dillon }
205921864bc5SMatthew Dillon
206021864bc5SMatthew Dillon /*
206121864bc5SMatthew Dillon * This function creates a new devfs node for a given device. It can
206221864bc5SMatthew Dillon * handle a complete path as device name, and accordingly creates
206321864bc5SMatthew Dillon * the path and the final device node.
2064ca8d7677SMatthew Dillon *
2065ca8d7677SMatthew Dillon * The reference count on the passed dev remains unchanged.
206621864bc5SMatthew Dillon */
206721864bc5SMatthew Dillon struct devfs_node *
devfs_create_device_node(struct devfs_node * root,cdev_t dev,int * existsp,char * dev_name,char * path_fmt,...)2068ca8d7677SMatthew Dillon devfs_create_device_node(struct devfs_node *root, cdev_t dev,
20692c94b9eeSMatthew Dillon int *existsp, char *dev_name, char *path_fmt, ...)
207021864bc5SMatthew Dillon {
207121864bc5SMatthew Dillon struct devfs_node *parent, *node = NULL;
207221864bc5SMatthew Dillon char *path = NULL;
2073da655383SMatthew Dillon char *name;
2074da655383SMatthew Dillon char *name_buf;
207521864bc5SMatthew Dillon __va_list ap;
207621864bc5SMatthew Dillon int i, found;
207721864bc5SMatthew Dillon char *create_path = NULL;
207821864bc5SMatthew Dillon char *names = "pqrsPQRS";
207921864bc5SMatthew Dillon
2080da655383SMatthew Dillon name_buf = kmalloc(PATH_MAX, M_TEMP, M_WAITOK);
208121864bc5SMatthew Dillon
20822c94b9eeSMatthew Dillon if (existsp)
20832c94b9eeSMatthew Dillon *existsp = 0;
20842c94b9eeSMatthew Dillon
2085da655383SMatthew Dillon if (path_fmt != NULL) {
208621864bc5SMatthew Dillon __va_start(ap, path_fmt);
2087ae71ac09Szrj kvasnprintf(&path, PATH_MAX, path_fmt, ap);
208821864bc5SMatthew Dillon __va_end(ap);
208921864bc5SMatthew Dillon }
209021864bc5SMatthew Dillon
209121864bc5SMatthew Dillon parent = devfs_resolve_or_create_path(root, path, 1);
209221864bc5SMatthew Dillon KKASSERT(parent);
209321864bc5SMatthew Dillon
2094bc185c5aSAlex Hornung devfs_resolve_name_path(
2095bc185c5aSAlex Hornung ((dev_name == NULL) && (dev))?(dev->si_name):(dev_name),
2096bc185c5aSAlex Hornung name_buf, &create_path, &name);
209721864bc5SMatthew Dillon
209821864bc5SMatthew Dillon if (create_path)
209921864bc5SMatthew Dillon parent = devfs_resolve_or_create_path(parent, create_path, 1);
210021864bc5SMatthew Dillon
210121864bc5SMatthew Dillon
21022c94b9eeSMatthew Dillon node = devfs_find_device_node_by_name(parent, name);
21032c94b9eeSMatthew Dillon if (node) {
21042c94b9eeSMatthew Dillon if (node->d_dev == dev) {
21052c94b9eeSMatthew Dillon /*
21062c94b9eeSMatthew Dillon * Allow case where device caches dev after the
21072c94b9eeSMatthew Dillon * close and might desire to reuse it.
21082c94b9eeSMatthew Dillon */
21092c94b9eeSMatthew Dillon if (existsp)
21102c94b9eeSMatthew Dillon *existsp = 1;
21112c94b9eeSMatthew Dillon } else {
21122c94b9eeSMatthew Dillon devfs_debug(DEVFS_DEBUG_WARNING,
21132c94b9eeSMatthew Dillon "devfs_create_device_node: "
21142c94b9eeSMatthew Dillon "DEVICE %s ALREADY EXISTS!!! "
21152c94b9eeSMatthew Dillon "Ignoring creation request.\n",
21162c94b9eeSMatthew Dillon name);
21172c94b9eeSMatthew Dillon node = NULL;
21182c94b9eeSMatthew Dillon }
211921864bc5SMatthew Dillon goto out;
212021864bc5SMatthew Dillon }
2121bc185c5aSAlex Hornung
21228e78a293SSascha Wildner node = devfs_allocp(Ndev, name, parent, parent->mp, dev);
2123d489a79aSMatthew Dillon vfs_timestamp(&parent->mtime);
21240182b316SAlex Hornung
2125bc185c5aSAlex Hornung /*
2126bc185c5aSAlex Hornung * Ugly unix98 pty magic, to hide pty master (ptm) devices and their
2127bc185c5aSAlex Hornung * directory
2128bc185c5aSAlex Hornung */
2129bc185c5aSAlex Hornung if ((dev) && (strlen(dev->si_name) >= 4) &&
2130bc185c5aSAlex Hornung (!memcmp(dev->si_name, "ptm/", 4))) {
2131894bbb25SAlex Hornung node->parent->flags |= DEVFS_HIDDEN;
2132894bbb25SAlex Hornung node->flags |= DEVFS_HIDDEN;
213321864bc5SMatthew Dillon }
2134bc185c5aSAlex Hornung
2135bc185c5aSAlex Hornung /*
2136bc185c5aSAlex Hornung * Ugly pty magic, to tag pty devices as such and hide them if needed.
2137bc185c5aSAlex Hornung */
213821864bc5SMatthew Dillon if ((strlen(name) >= 3) && (!memcmp(name, "pty", 3)))
213921864bc5SMatthew Dillon node->flags |= (DEVFS_PTY | DEVFS_INVISIBLE);
214021864bc5SMatthew Dillon
214121864bc5SMatthew Dillon if ((strlen(name) >= 3) && (!memcmp(name, "tty", 3))) {
214221864bc5SMatthew Dillon found = 0;
214321864bc5SMatthew Dillon for (i = 0; i < strlen(names); i++) {
214421864bc5SMatthew Dillon if (name[3] == names[i]) {
214521864bc5SMatthew Dillon found = 1;
214621864bc5SMatthew Dillon break;
214721864bc5SMatthew Dillon }
214821864bc5SMatthew Dillon }
214921864bc5SMatthew Dillon if (found)
215021864bc5SMatthew Dillon node->flags |= (DEVFS_PTY | DEVFS_INVISIBLE);
215121864bc5SMatthew Dillon }
215221864bc5SMatthew Dillon
215321864bc5SMatthew Dillon out:
2154da655383SMatthew Dillon kfree(name_buf, M_TEMP);
2155da655383SMatthew Dillon kvasfree(&path);
215621864bc5SMatthew Dillon return node;
215721864bc5SMatthew Dillon }
215821864bc5SMatthew Dillon
215921864bc5SMatthew Dillon /*
216021864bc5SMatthew Dillon * This function finds a given device node in the topology with a given
216121864bc5SMatthew Dillon * cdev.
216221864bc5SMatthew Dillon */
216366abefa5SAlex Hornung void *
devfs_find_device_node_callback(struct devfs_node * node,cdev_t target)216466abefa5SAlex Hornung devfs_find_device_node_callback(struct devfs_node *node, cdev_t target)
216521864bc5SMatthew Dillon {
21668e78a293SSascha Wildner if ((node->node_type == Ndev) && (node->d_dev == target)) {
216721864bc5SMatthew Dillon return node;
216821864bc5SMatthew Dillon }
216921864bc5SMatthew Dillon
217021864bc5SMatthew Dillon return NULL;
217121864bc5SMatthew Dillon }
217221864bc5SMatthew Dillon
217321864bc5SMatthew Dillon /*
217466abefa5SAlex Hornung * This function finds a device node in the given parent directory by its
217521864bc5SMatthew Dillon * name and returns it.
217621864bc5SMatthew Dillon */
217721864bc5SMatthew Dillon struct devfs_node *
devfs_find_device_node_by_name(struct devfs_node * parent,char * target)217821864bc5SMatthew Dillon devfs_find_device_node_by_name(struct devfs_node *parent, char *target)
217921864bc5SMatthew Dillon {
218021864bc5SMatthew Dillon struct devfs_node *node, *found = NULL;
218121864bc5SMatthew Dillon size_t len = strlen(target);
218221864bc5SMatthew Dillon
218321864bc5SMatthew Dillon TAILQ_FOREACH(node, DEVFS_DENODE_HEAD(parent), link) {
2184bc185c5aSAlex Hornung if (len != node->d_dir.d_namlen)
2185bc185c5aSAlex Hornung continue;
2186bc185c5aSAlex Hornung
2187bc185c5aSAlex Hornung if (!memcmp(node->d_dir.d_name, target, len)) {
218821864bc5SMatthew Dillon found = node;
218921864bc5SMatthew Dillon break;
219021864bc5SMatthew Dillon }
219121864bc5SMatthew Dillon }
219221864bc5SMatthew Dillon
219321864bc5SMatthew Dillon return found;
219421864bc5SMatthew Dillon }
219521864bc5SMatthew Dillon
219666abefa5SAlex Hornung static void *
devfs_inode_to_vnode_worker_callback(struct devfs_node * node,ino_t * inop)219766abefa5SAlex Hornung devfs_inode_to_vnode_worker_callback(struct devfs_node *node, ino_t *inop)
2198fa7e6f37SAlex Hornung {
219966abefa5SAlex Hornung struct vnode *vp = NULL;
220066abefa5SAlex Hornung ino_t target = *inop;
2201bc185c5aSAlex Hornung
2202fa7e6f37SAlex Hornung if (node->d_dir.d_ino == target) {
2203fa7e6f37SAlex Hornung if (node->v_node) {
2204fa7e6f37SAlex Hornung vp = node->v_node;
2205fa7e6f37SAlex Hornung vget(vp, LK_EXCLUSIVE | LK_RETRY);
2206fa7e6f37SAlex Hornung vn_unlock(vp);
2207fa7e6f37SAlex Hornung } else {
2208fa7e6f37SAlex Hornung devfs_allocv(&vp, node);
2209fa7e6f37SAlex Hornung vn_unlock(vp);
2210fa7e6f37SAlex Hornung }
2211fa7e6f37SAlex Hornung }
2212fa7e6f37SAlex Hornung
221366abefa5SAlex Hornung return vp;
2214fa7e6f37SAlex Hornung }
2215fa7e6f37SAlex Hornung
221621864bc5SMatthew Dillon /*
2217ca8d7677SMatthew Dillon * This function takes a cdev and removes its devfs node in the
2218ca8d7677SMatthew Dillon * given topology. The cdev remains intact.
221921864bc5SMatthew Dillon */
222021864bc5SMatthew Dillon int
devfs_destroy_device_node(struct devfs_node * root,cdev_t target)222121864bc5SMatthew Dillon devfs_destroy_device_node(struct devfs_node *root, cdev_t target)
222221864bc5SMatthew Dillon {
22238312ca30SAlex Hornung KKASSERT(target != NULL);
22248312ca30SAlex Hornung return devfs_destroy_node(root, target->si_name);
22258312ca30SAlex Hornung }
22268312ca30SAlex Hornung
22278312ca30SAlex Hornung /*
22288312ca30SAlex Hornung * This function takes a path to a devfs node, resolves it and
22298312ca30SAlex Hornung * removes the devfs node from the given topology.
22308312ca30SAlex Hornung */
22318312ca30SAlex Hornung int
devfs_destroy_node(struct devfs_node * root,char * target)22328312ca30SAlex Hornung devfs_destroy_node(struct devfs_node *root, char *target)
22338312ca30SAlex Hornung {
223421864bc5SMatthew Dillon struct devfs_node *node, *parent;
2235da655383SMatthew Dillon char *name;
2236da655383SMatthew Dillon char *name_buf;
223721864bc5SMatthew Dillon char *create_path = NULL;
223821864bc5SMatthew Dillon
223921864bc5SMatthew Dillon KKASSERT(target);
224021864bc5SMatthew Dillon
2241da655383SMatthew Dillon name_buf = kmalloc(PATH_MAX, M_TEMP, M_WAITOK);
22428312ca30SAlex Hornung ksnprintf(name_buf, PATH_MAX, "%s", target);
224321864bc5SMatthew Dillon
22448312ca30SAlex Hornung devfs_resolve_name_path(target, name_buf, &create_path, &name);
224521864bc5SMatthew Dillon
224621864bc5SMatthew Dillon if (create_path)
224721864bc5SMatthew Dillon parent = devfs_resolve_or_create_path(root, create_path, 0);
224821864bc5SMatthew Dillon else
224921864bc5SMatthew Dillon parent = root;
2250bc185c5aSAlex Hornung
2251ab3436e7SMatthew Dillon if (parent == NULL) {
2252ab3436e7SMatthew Dillon kfree(name_buf, M_TEMP);
225321864bc5SMatthew Dillon return 1;
2254ab3436e7SMatthew Dillon }
2255bc185c5aSAlex Hornung
225621864bc5SMatthew Dillon node = devfs_find_device_node_by_name(parent, name);
2257bc185c5aSAlex Hornung
225807dfa375SAlex Hornung if (node) {
2259d489a79aSMatthew Dillon vfs_timestamp(&node->parent->mtime);
226021864bc5SMatthew Dillon devfs_gc(node);
226107dfa375SAlex Hornung }
2262115f9a72SAlexander Polakov
2263da655383SMatthew Dillon kfree(name_buf, M_TEMP);
226421864bc5SMatthew Dillon
226521864bc5SMatthew Dillon return 0;
226621864bc5SMatthew Dillon }
226721864bc5SMatthew Dillon
226821864bc5SMatthew Dillon /*
226921864bc5SMatthew Dillon * Just set perms and ownership for given node.
227021864bc5SMatthew Dillon */
227121864bc5SMatthew Dillon int
devfs_set_perms(struct devfs_node * node,uid_t uid,gid_t gid,u_short mode,u_long flags)2272bc185c5aSAlex Hornung devfs_set_perms(struct devfs_node *node, uid_t uid, gid_t gid,
2273bc185c5aSAlex Hornung u_short mode, u_long flags)
227421864bc5SMatthew Dillon {
2275bc185c5aSAlex Hornung node->mode = mode;
2276bc185c5aSAlex Hornung node->uid = uid;
2277bc185c5aSAlex Hornung node->gid = gid;
227821864bc5SMatthew Dillon
227921864bc5SMatthew Dillon return 0;
228021864bc5SMatthew Dillon }
228121864bc5SMatthew Dillon
228221864bc5SMatthew Dillon /*
228321864bc5SMatthew Dillon * Propagates a device attach/detach to all mount
228421864bc5SMatthew Dillon * points. Also takes care of automatic alias removal
228521864bc5SMatthew Dillon * for a deleted cdev.
228621864bc5SMatthew Dillon */
228721864bc5SMatthew Dillon static int
devfs_propagate_dev(cdev_t dev,int attach)228821864bc5SMatthew Dillon devfs_propagate_dev(cdev_t dev, int attach)
228921864bc5SMatthew Dillon {
229021864bc5SMatthew Dillon struct devfs_mnt_data *mnt;
229121864bc5SMatthew Dillon
229221864bc5SMatthew Dillon TAILQ_FOREACH(mnt, &devfs_mnt_list, link) {
229321864bc5SMatthew Dillon if (attach) {
229421864bc5SMatthew Dillon /* Device is being attached */
2295ca8d7677SMatthew Dillon devfs_create_device_node(mnt->root_node, dev,
22962c94b9eeSMatthew Dillon NULL, NULL, NULL);
229721864bc5SMatthew Dillon } else {
229821864bc5SMatthew Dillon /* Device is being detached */
229921864bc5SMatthew Dillon devfs_alias_remove(dev);
230021864bc5SMatthew Dillon devfs_destroy_device_node(mnt->root_node, dev);
230121864bc5SMatthew Dillon }
230221864bc5SMatthew Dillon }
230321864bc5SMatthew Dillon return 0;
230421864bc5SMatthew Dillon }
230521864bc5SMatthew Dillon
230621864bc5SMatthew Dillon /*
230721864bc5SMatthew Dillon * devfs_clone either returns a basename from a complete name by
230821864bc5SMatthew Dillon * returning the length of the name without trailing digits, or,
230921864bc5SMatthew Dillon * if clone != 0, calls the device's clone handler to get a new
231021864bc5SMatthew Dillon * device, which in turn is returned in devp.
2311845bd036SMatthew Dillon *
2312845bd036SMatthew Dillon * Caller must hold a shared devfs_lock
231321864bc5SMatthew Dillon */
231407dfa375SAlex Hornung cdev_t
devfs_clone(cdev_t dev,const char * name,size_t len,int mode,struct ucred * cred)231507dfa375SAlex Hornung devfs_clone(cdev_t dev, const char *name, size_t len, int mode,
2316bc185c5aSAlex Hornung struct ucred *cred)
231721864bc5SMatthew Dillon {
231807dfa375SAlex Hornung int error;
231921864bc5SMatthew Dillon struct devfs_clone_handler *chandler;
232021864bc5SMatthew Dillon struct dev_clone_args ap;
232121864bc5SMatthew Dillon
232221864bc5SMatthew Dillon TAILQ_FOREACH(chandler, &devfs_chandler_list, link) {
232307dfa375SAlex Hornung if (chandler->namlen != len)
232407dfa375SAlex Hornung continue;
23253e701205SMatthew Dillon if ((!memcmp(chandler->name, name, len)) &&
23263e701205SMatthew Dillon (chandler->nhandler)) {
23273e701205SMatthew Dillon /*
23283e701205SMatthew Dillon * We have to unlock across the config and the
23293e701205SMatthew Dillon * callback to avoid deadlocking. The device is
23303e701205SMatthew Dillon * likely to obtain its own lock in the callback
23313e701205SMatthew Dillon * and might then call into devfs.
23323e701205SMatthew Dillon */
233307dfa375SAlex Hornung lockmgr(&devfs_lock, LK_RELEASE);
233407dfa375SAlex Hornung devfs_config();
233507dfa375SAlex Hornung ap.a_head.a_dev = dev;
233621864bc5SMatthew Dillon ap.a_dev = NULL;
233721864bc5SMatthew Dillon ap.a_name = name;
233821864bc5SMatthew Dillon ap.a_namelen = len;
233907dfa375SAlex Hornung ap.a_mode = mode;
234021864bc5SMatthew Dillon ap.a_cred = cred;
234121864bc5SMatthew Dillon error = (chandler->nhandler)(&ap);
2342845bd036SMatthew Dillon lockmgr(&devfs_lock, LK_SHARED);
234307dfa375SAlex Hornung if (error)
234407dfa375SAlex Hornung continue;
234521864bc5SMatthew Dillon
234607dfa375SAlex Hornung return ap.a_dev;
234721864bc5SMatthew Dillon }
234821864bc5SMatthew Dillon }
234921864bc5SMatthew Dillon
235007dfa375SAlex Hornung return NULL;
235121864bc5SMatthew Dillon }
235221864bc5SMatthew Dillon
235321864bc5SMatthew Dillon
235421864bc5SMatthew Dillon /*
235521864bc5SMatthew Dillon * Registers a new orphan in the orphan list.
235621864bc5SMatthew Dillon */
235721864bc5SMatthew Dillon void
devfs_tracer_add_orphan(struct devfs_node * node)235821864bc5SMatthew Dillon devfs_tracer_add_orphan(struct devfs_node *node)
235921864bc5SMatthew Dillon {
236021864bc5SMatthew Dillon struct devfs_orphan *orphan;
236121864bc5SMatthew Dillon
236221864bc5SMatthew Dillon KKASSERT(node);
236321864bc5SMatthew Dillon orphan = kmalloc(sizeof(struct devfs_orphan), M_DEVFS, M_WAITOK);
236421864bc5SMatthew Dillon orphan->node = node;
236521864bc5SMatthew Dillon
2366ca8d7677SMatthew Dillon KKASSERT((node->flags & DEVFS_ORPHANED) == 0);
2367ca8d7677SMatthew Dillon node->flags |= DEVFS_ORPHANED;
236821864bc5SMatthew Dillon TAILQ_INSERT_TAIL(DEVFS_ORPHANLIST(node->mp), orphan, link);
236921864bc5SMatthew Dillon }
237021864bc5SMatthew Dillon
237121864bc5SMatthew Dillon /*
237221864bc5SMatthew Dillon * Removes an orphan from the orphan list.
237321864bc5SMatthew Dillon */
237421864bc5SMatthew Dillon void
devfs_tracer_del_orphan(struct devfs_node * node)237521864bc5SMatthew Dillon devfs_tracer_del_orphan(struct devfs_node *node)
237621864bc5SMatthew Dillon {
237721864bc5SMatthew Dillon struct devfs_orphan *orphan;
237821864bc5SMatthew Dillon
237921864bc5SMatthew Dillon KKASSERT(node);
238021864bc5SMatthew Dillon
238121864bc5SMatthew Dillon TAILQ_FOREACH(orphan, DEVFS_ORPHANLIST(node->mp), link) {
238221864bc5SMatthew Dillon if (orphan->node == node) {
2383ca8d7677SMatthew Dillon node->flags &= ~DEVFS_ORPHANED;
238421864bc5SMatthew Dillon TAILQ_REMOVE(DEVFS_ORPHANLIST(node->mp), orphan, link);
238521864bc5SMatthew Dillon kfree(orphan, M_DEVFS);
238621864bc5SMatthew Dillon break;
238721864bc5SMatthew Dillon }
238821864bc5SMatthew Dillon }
238921864bc5SMatthew Dillon }
239021864bc5SMatthew Dillon
239121864bc5SMatthew Dillon /*
239221864bc5SMatthew Dillon * Counts the orphans in the orphan list, and if cleanup
239321864bc5SMatthew Dillon * is specified, also frees the orphan and removes it from
239421864bc5SMatthew Dillon * the list.
239521864bc5SMatthew Dillon */
239621864bc5SMatthew Dillon size_t
devfs_tracer_orphan_count(struct mount * mp,int cleanup)239721864bc5SMatthew Dillon devfs_tracer_orphan_count(struct mount *mp, int cleanup)
239821864bc5SMatthew Dillon {
239921864bc5SMatthew Dillon struct devfs_orphan *orphan, *orphan2;
240021864bc5SMatthew Dillon size_t count = 0;
240121864bc5SMatthew Dillon
240221864bc5SMatthew Dillon TAILQ_FOREACH_MUTABLE(orphan, DEVFS_ORPHANLIST(mp), link, orphan2) {
240321864bc5SMatthew Dillon count++;
2404bc185c5aSAlex Hornung /*
2405bc185c5aSAlex Hornung * If we are instructed to clean up, we do so.
2406bc185c5aSAlex Hornung */
240721864bc5SMatthew Dillon if (cleanup) {
240821864bc5SMatthew Dillon TAILQ_REMOVE(DEVFS_ORPHANLIST(mp), orphan, link);
2409ca8d7677SMatthew Dillon orphan->node->flags &= ~DEVFS_ORPHANED;
2410ca8d7677SMatthew Dillon devfs_freep(orphan->node);
241121864bc5SMatthew Dillon kfree(orphan, M_DEVFS);
241221864bc5SMatthew Dillon }
241321864bc5SMatthew Dillon }
241421864bc5SMatthew Dillon
241521864bc5SMatthew Dillon return count;
241621864bc5SMatthew Dillon }
241721864bc5SMatthew Dillon
241821864bc5SMatthew Dillon /*
241921864bc5SMatthew Dillon * Fetch an ino_t from the global d_ino by increasing it
242021864bc5SMatthew Dillon * while spinlocked.
242121864bc5SMatthew Dillon */
242221864bc5SMatthew Dillon static ino_t
devfs_fetch_ino(void)242321864bc5SMatthew Dillon devfs_fetch_ino(void)
242421864bc5SMatthew Dillon {
242521864bc5SMatthew Dillon ino_t ret;
242621864bc5SMatthew Dillon
2427287a8577SAlex Hornung spin_lock(&ino_lock);
242821864bc5SMatthew Dillon ret = d_ino++;
2429287a8577SAlex Hornung spin_unlock(&ino_lock);
243021864bc5SMatthew Dillon
243121864bc5SMatthew Dillon return ret;
243221864bc5SMatthew Dillon }
243321864bc5SMatthew Dillon
243421864bc5SMatthew Dillon /*
243521864bc5SMatthew Dillon * Allocates a new cdev and initializes it's most basic
243621864bc5SMatthew Dillon * fields.
243721864bc5SMatthew Dillon */
243821864bc5SMatthew Dillon cdev_t
devfs_new_cdev(struct dev_ops * ops,int minor,struct dev_ops * bops)24398f960aa9SAlex Hornung devfs_new_cdev(struct dev_ops *ops, int minor, struct dev_ops *bops)
244021864bc5SMatthew Dillon {
244121864bc5SMatthew Dillon cdev_t dev = sysref_alloc(&cdev_sysref_class);
2442da655383SMatthew Dillon
244321864bc5SMatthew Dillon sysref_activate(&dev->si_sysref);
244421864bc5SMatthew Dillon reference_dev(dev);
2445da655383SMatthew Dillon bzero(dev, offsetof(struct cdev, si_sysref));
244621864bc5SMatthew Dillon
2447d1a689c1SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
2448d1a689c1SMatthew Dillon
244921864bc5SMatthew Dillon dev->si_uid = 0;
245021864bc5SMatthew Dillon dev->si_gid = 0;
245121864bc5SMatthew Dillon dev->si_perms = 0;
245221864bc5SMatthew Dillon dev->si_drv1 = NULL;
245321864bc5SMatthew Dillon dev->si_drv2 = NULL;
2454cec73927SMatthew Dillon dev->si_lastread = 0; /* time_uptime */
2455cec73927SMatthew Dillon dev->si_lastwrite = 0; /* time_uptime */
245621864bc5SMatthew Dillon
2457f5d8307cSAlex Hornung dev->si_dict = NULL;
245872ea429eSAlex Hornung dev->si_parent = NULL;
245921864bc5SMatthew Dillon dev->si_ops = ops;
2460894bbb25SAlex Hornung dev->si_flags = 0;
246121864bc5SMatthew Dillon dev->si_uminor = minor;
24628f960aa9SAlex Hornung dev->si_bops = bops;
2463bf390b25SAlex Hornung
2464bf390b25SAlex Hornung /*
2465bf390b25SAlex Hornung * Since the disk subsystem is in the way, we need to
2466bf390b25SAlex Hornung * propagate the D_CANFREE from bops (and ops) to
2467bf390b25SAlex Hornung * si_flags.
2468bf390b25SAlex Hornung */
2469bf390b25SAlex Hornung if (bops && (bops->head.flags & D_CANFREE)) {
2470bf390b25SAlex Hornung dev->si_flags |= SI_CANFREE;
2471bf390b25SAlex Hornung } else if (ops->head.flags & D_CANFREE) {
2472bf390b25SAlex Hornung dev->si_flags |= SI_CANFREE;
2473bf390b25SAlex Hornung }
2474bf390b25SAlex Hornung
247547ae500fSAlex Hornung /* If there is a backing device, we reference its ops */
2476dd0e3cd7SMatthew Dillon if (bops == NULL)
2477dd0e3cd7SMatthew Dillon bops = ops;
2478dd0e3cd7SMatthew Dillon dev->si_inode = makeudev(devfs_reference_ops(bops), minor);
2479619e8f47SSascha Wildner dev->si_umajor = umajor(dev->si_inode);
248021864bc5SMatthew Dillon
2481d1a689c1SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
2482d1a689c1SMatthew Dillon
248321864bc5SMatthew Dillon return dev;
248421864bc5SMatthew Dillon }
248521864bc5SMatthew Dillon
2486ca8d7677SMatthew Dillon static void
devfs_cdev_terminate(cdev_t dev)2487ca8d7677SMatthew Dillon devfs_cdev_terminate(cdev_t dev)
248821864bc5SMatthew Dillon {
2489e4ff5ef9SAlex Hornung /*
2490e4ff5ef9SAlex Hornung * Make sure the node isn't linked anymore. Otherwise we've screwed
2491e4ff5ef9SAlex Hornung * up somewhere, since normal devs are unlinked on the call to
2492e4ff5ef9SAlex Hornung * destroy_dev and only-cdevs that have not been used for cloning
2493e4ff5ef9SAlex Hornung * are not linked in the first place. only-cdevs used for cloning
2494e4ff5ef9SAlex Hornung * will be linked in, too, and should only be destroyed via
2495e4ff5ef9SAlex Hornung * destroy_dev, not destroy_only_dev, so we catch that problem, too.
2496e4ff5ef9SAlex Hornung */
2497e4ff5ef9SAlex Hornung KKASSERT((dev->si_flags & SI_DEVFS_LINKED) == 0);
249821864bc5SMatthew Dillon
249947ae500fSAlex Hornung /* If there is a backing device, we release the backing device's ops */
25008f960aa9SAlex Hornung devfs_release_ops((dev->si_bops)?(dev->si_bops):(dev->si_ops));
25017cbab9daSAlex Hornung
2502dd0e3cd7SMatthew Dillon /* devfs_cdev_unlock() is not called, unlock ourselves */
2503dd0e3cd7SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
2504dd0e3cd7SMatthew Dillon
250521864bc5SMatthew Dillon /* Finally destroy the device */
250621864bc5SMatthew Dillon sysref_put(&dev->si_sysref);
250721864bc5SMatthew Dillon }
250821864bc5SMatthew Dillon
250921864bc5SMatthew Dillon /*
2510e654922cSMatthew Dillon * Dummies for now (individual locks for MPSAFE)
2511e654922cSMatthew Dillon */
2512e654922cSMatthew Dillon static void
devfs_cdev_lock(cdev_t dev)2513e654922cSMatthew Dillon devfs_cdev_lock(cdev_t dev)
2514e654922cSMatthew Dillon {
2515dd0e3cd7SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
2516e654922cSMatthew Dillon }
2517e654922cSMatthew Dillon
2518e654922cSMatthew Dillon static void
devfs_cdev_unlock(cdev_t dev)2519e654922cSMatthew Dillon devfs_cdev_unlock(cdev_t dev)
2520e654922cSMatthew Dillon {
2521dd0e3cd7SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
2522e654922cSMatthew Dillon }
2523e654922cSMatthew Dillon
25243bd9e88aSSamuel J. Greear static int
devfs_detached_filter_eof(struct knote * kn,long hint)25253bd9e88aSSamuel J. Greear devfs_detached_filter_eof(struct knote *kn, long hint)
25263bd9e88aSSamuel J. Greear {
25273bcb6e5eSSepherosa Ziehau kn->kn_flags |= (EV_EOF | EV_NODATA);
25283bd9e88aSSamuel J. Greear return (1);
25293bd9e88aSSamuel J. Greear }
25303bd9e88aSSamuel J. Greear
25313bd9e88aSSamuel J. Greear static void
devfs_detached_filter_detach(struct knote * kn)25323bd9e88aSSamuel J. Greear devfs_detached_filter_detach(struct knote *kn)
25333bd9e88aSSamuel J. Greear {
25343bd9e88aSSamuel J. Greear cdev_t dev = (cdev_t)kn->kn_hook;
25353bd9e88aSSamuel J. Greear
25363bd9e88aSSamuel J. Greear knote_remove(&dev->si_kqinfo.ki_note, kn);
25373bd9e88aSSamuel J. Greear }
25383bd9e88aSSamuel J. Greear
25393bd9e88aSSamuel J. Greear static struct filterops devfs_detached_filterops =
25403bd9e88aSSamuel J. Greear { FILTEROP_ISFD, NULL,
25413bd9e88aSSamuel J. Greear devfs_detached_filter_detach,
25423bd9e88aSSamuel J. Greear devfs_detached_filter_eof };
25433bd9e88aSSamuel J. Greear
25443bd9e88aSSamuel J. Greear /*
25453bd9e88aSSamuel J. Greear * Delegates knote filter handling responsibility to devfs
25463bd9e88aSSamuel J. Greear *
25473bd9e88aSSamuel J. Greear * Any device that implements kqfilter event handling and could be detached
25483bd9e88aSSamuel J. Greear * or shut down out from under the kevent subsystem must allow devfs to
25493bd9e88aSSamuel J. Greear * assume responsibility for any knotes it may hold.
25503bd9e88aSSamuel J. Greear */
25513bd9e88aSSamuel J. Greear void
devfs_assume_knotes(cdev_t dev,struct kqinfo * kqi)25523bd9e88aSSamuel J. Greear devfs_assume_knotes(cdev_t dev, struct kqinfo *kqi)
25533bd9e88aSSamuel J. Greear {
2554377c3461SMatthew Dillon /*
2555377c3461SMatthew Dillon * Let kern/kern_event.c do the heavy lifting.
2556377c3461SMatthew Dillon */
2557377c3461SMatthew Dillon knote_assume_knotes(kqi, &dev->si_kqinfo,
2558377c3461SMatthew Dillon &devfs_detached_filterops, (void *)dev);
25593bd9e88aSSamuel J. Greear
25603bd9e88aSSamuel J. Greear /*
25613bd9e88aSSamuel J. Greear * These should probably be activated individually, but doing so
25623bd9e88aSSamuel J. Greear * would require refactoring kq's public in-kernel interface.
25633bd9e88aSSamuel J. Greear */
25643bd9e88aSSamuel J. Greear KNOTE(&dev->si_kqinfo.ki_note, 0);
25653bd9e88aSSamuel J. Greear }
25663bd9e88aSSamuel J. Greear
2567e654922cSMatthew Dillon /*
256821864bc5SMatthew Dillon * Links a given cdev into the dev list.
256921864bc5SMatthew Dillon */
257021864bc5SMatthew Dillon int
devfs_link_dev(cdev_t dev)257121864bc5SMatthew Dillon devfs_link_dev(cdev_t dev)
257221864bc5SMatthew Dillon {
2573ca8d7677SMatthew Dillon KKASSERT((dev->si_flags & SI_DEVFS_LINKED) == 0);
257421864bc5SMatthew Dillon dev->si_flags |= SI_DEVFS_LINKED;
257521864bc5SMatthew Dillon TAILQ_INSERT_TAIL(&devfs_dev_list, dev, link);
257621864bc5SMatthew Dillon
257721864bc5SMatthew Dillon return 0;
257821864bc5SMatthew Dillon }
257921864bc5SMatthew Dillon
258021864bc5SMatthew Dillon /*
2581ca8d7677SMatthew Dillon * Removes a given cdev from the dev list. The caller is responsible for
2582ca8d7677SMatthew Dillon * releasing the reference on the device associated with the linkage.
2583ca8d7677SMatthew Dillon *
2584ca8d7677SMatthew Dillon * Returns EALREADY if the dev has already been unlinked.
258521864bc5SMatthew Dillon */
2586ca8d7677SMatthew Dillon static int
devfs_unlink_dev(cdev_t dev)258721864bc5SMatthew Dillon devfs_unlink_dev(cdev_t dev)
258821864bc5SMatthew Dillon {
258921864bc5SMatthew Dillon if ((dev->si_flags & SI_DEVFS_LINKED)) {
259021864bc5SMatthew Dillon TAILQ_REMOVE(&devfs_dev_list, dev, link);
259121864bc5SMatthew Dillon dev->si_flags &= ~SI_DEVFS_LINKED;
2592ca8d7677SMatthew Dillon return (0);
259321864bc5SMatthew Dillon }
2594ca8d7677SMatthew Dillon return (EALREADY);
259521864bc5SMatthew Dillon }
259621864bc5SMatthew Dillon
2597894bbb25SAlex Hornung int
devfs_node_is_accessible(struct devfs_node * node)2598894bbb25SAlex Hornung devfs_node_is_accessible(struct devfs_node *node)
2599894bbb25SAlex Hornung {
2600894bbb25SAlex Hornung if ((node) && (!(node->flags & DEVFS_HIDDEN)))
2601894bbb25SAlex Hornung return 1;
2602894bbb25SAlex Hornung else
2603894bbb25SAlex Hornung return 0;
2604894bbb25SAlex Hornung }
2605894bbb25SAlex Hornung
2606d1a689c1SMatthew Dillon /*
2607d1a689c1SMatthew Dillon * devfs must be locked
2608d1a689c1SMatthew Dillon */
2609dd0e3cd7SMatthew Dillon static int
devfs_reference_ops(struct dev_ops * ops)26107cbab9daSAlex Hornung devfs_reference_ops(struct dev_ops *ops)
26117cbab9daSAlex Hornung {
26127cbab9daSAlex Hornung int unit;
2613176de024SAlex Hornung struct devfs_dev_ops *found = NULL;
2614176de024SAlex Hornung struct devfs_dev_ops *devops;
26157cbab9daSAlex Hornung
2616176de024SAlex Hornung TAILQ_FOREACH(devops, &devfs_dev_ops_list, link) {
2617176de024SAlex Hornung if (devops->ops == ops) {
2618176de024SAlex Hornung found = devops;
2619176de024SAlex Hornung break;
2620176de024SAlex Hornung }
2621176de024SAlex Hornung }
2622176de024SAlex Hornung
2623176de024SAlex Hornung if (!found) {
2624d1a689c1SMatthew Dillon found = kmalloc(sizeof(struct devfs_dev_ops),
2625d1a689c1SMatthew Dillon M_DEVFS, M_WAITOK);
2626176de024SAlex Hornung found->ops = ops;
2627176de024SAlex Hornung found->ref_count = 0;
2628176de024SAlex Hornung TAILQ_INSERT_TAIL(&devfs_dev_ops_list, found, link);
2629176de024SAlex Hornung }
2630176de024SAlex Hornung
2631176de024SAlex Hornung KKASSERT(found);
2632176de024SAlex Hornung
2633176de024SAlex Hornung if (found->ref_count == 0) {
2634d1a689c1SMatthew Dillon found->id =
2635d1a689c1SMatthew Dillon devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(ops_id), 255);
2636176de024SAlex Hornung if (found->id == -1) {
26377cbab9daSAlex Hornung /* Ran out of unique ids */
2638bc185c5aSAlex Hornung devfs_debug(DEVFS_DEBUG_WARNING,
2639dd0e3cd7SMatthew Dillon "devfs_reference_ops: WARNING: ran "
2640dd0e3cd7SMatthew Dillon "out of unique ids\n");
26417cbab9daSAlex Hornung }
26427cbab9daSAlex Hornung }
2643176de024SAlex Hornung unit = found->id;
2644176de024SAlex Hornung ++found->ref_count;
26457cbab9daSAlex Hornung
26467cbab9daSAlex Hornung return unit;
26477cbab9daSAlex Hornung }
26487cbab9daSAlex Hornung
2649643d187bSMatthew Dillon /*
2650643d187bSMatthew Dillon * devfs must be locked
2651643d187bSMatthew Dillon */
2652dd0e3cd7SMatthew Dillon static void
devfs_release_ops(struct dev_ops * ops)26537cbab9daSAlex Hornung devfs_release_ops(struct dev_ops *ops)
26547cbab9daSAlex Hornung {
2655176de024SAlex Hornung struct devfs_dev_ops *found = NULL;
2656176de024SAlex Hornung struct devfs_dev_ops *devops;
26577cbab9daSAlex Hornung
2658176de024SAlex Hornung TAILQ_FOREACH(devops, &devfs_dev_ops_list, link) {
2659176de024SAlex Hornung if (devops->ops == ops) {
2660176de024SAlex Hornung found = devops;
2661176de024SAlex Hornung break;
2662176de024SAlex Hornung }
2663176de024SAlex Hornung }
2664176de024SAlex Hornung
2665176de024SAlex Hornung KKASSERT(found);
2666176de024SAlex Hornung
2667176de024SAlex Hornung --found->ref_count;
2668176de024SAlex Hornung
2669176de024SAlex Hornung if (found->ref_count == 0) {
2670176de024SAlex Hornung TAILQ_REMOVE(&devfs_dev_ops_list, found, link);
2671643d187bSMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
2672176de024SAlex Hornung devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(ops_id), found->id);
2673643d187bSMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
2674176de024SAlex Hornung kfree(found, M_DEVFS);
26757cbab9daSAlex Hornung }
26767cbab9daSAlex Hornung }
26777cbab9daSAlex Hornung
2678a4141af4SMatthew Dillon /*
2679a4141af4SMatthew Dillon * Wait for asynchronous messages to complete in the devfs helper
2680a4141af4SMatthew Dillon * thread, then return. Do nothing if the helper thread is dead
26811c375cd1SMatthew Dillon * or we are being indirectly called from the helper thread itself.
2682a4141af4SMatthew Dillon */
268321864bc5SMatthew Dillon void
devfs_config(void)2684d0fe8596SMatthew Dillon devfs_config(void)
268521864bc5SMatthew Dillon {
268621864bc5SMatthew Dillon devfs_msg_t msg;
268721864bc5SMatthew Dillon
26881c375cd1SMatthew Dillon if (devfs_run && curthread != td_core) {
268921864bc5SMatthew Dillon msg = devfs_msg_get();
26902e05d283SNuno Antunes devfs_msg_send_sync(DEVFS_SYNC, msg);
269121864bc5SMatthew Dillon devfs_msg_put(msg);
269221864bc5SMatthew Dillon }
2693a4141af4SMatthew Dillon }
269421864bc5SMatthew Dillon
269521864bc5SMatthew Dillon /*
269621864bc5SMatthew Dillon * Called on init of devfs; creates the objcaches and
269721864bc5SMatthew Dillon * spawns off the devfs core thread. Also initializes
269821864bc5SMatthew Dillon * locks.
269921864bc5SMatthew Dillon */
270021864bc5SMatthew Dillon static void
devfs_init(void)270121864bc5SMatthew Dillon devfs_init(void)
270221864bc5SMatthew Dillon {
270321864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_init() called\n");
270421864bc5SMatthew Dillon /* Create objcaches for nodes, msgs and devs */
270521864bc5SMatthew Dillon devfs_node_cache = objcache_create("devfs-node-cache", 0, 0,
270621864bc5SMatthew Dillon NULL, NULL, NULL,
270721864bc5SMatthew Dillon objcache_malloc_alloc,
270821864bc5SMatthew Dillon objcache_malloc_free,
270921864bc5SMatthew Dillon &devfs_node_malloc_args );
271021864bc5SMatthew Dillon
271121864bc5SMatthew Dillon devfs_msg_cache = objcache_create("devfs-msg-cache", 0, 0,
271221864bc5SMatthew Dillon NULL, NULL, NULL,
271321864bc5SMatthew Dillon objcache_malloc_alloc,
271421864bc5SMatthew Dillon objcache_malloc_free,
271521864bc5SMatthew Dillon &devfs_msg_malloc_args );
271621864bc5SMatthew Dillon
271721864bc5SMatthew Dillon devfs_dev_cache = objcache_create("devfs-dev-cache", 0, 0,
271821864bc5SMatthew Dillon NULL, NULL, NULL,
271921864bc5SMatthew Dillon objcache_malloc_alloc,
272021864bc5SMatthew Dillon objcache_malloc_free,
272121864bc5SMatthew Dillon &devfs_dev_malloc_args );
272221864bc5SMatthew Dillon
27237cbab9daSAlex Hornung devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(ops_id));
27247cbab9daSAlex Hornung
272521864bc5SMatthew Dillon /* Initialize the reply-only port which acts as a message drain */
272621864bc5SMatthew Dillon lwkt_initport_replyonly(&devfs_dispose_port, devfs_msg_autofree_reply);
272721864bc5SMatthew Dillon
272821864bc5SMatthew Dillon /* Initialize *THE* devfs lock */
2729dd0e3cd7SMatthew Dillon lockinit(&devfs_lock, "devfs_core lock", 0, LK_CANRECURSE);
2730c0739b3cSMatthew Dillon lwkt_token_init(&devfs_token, "devfs_core");
273121864bc5SMatthew Dillon
2732c9e9fb21SMatthew Dillon lockmgr(&devfs_lock, LK_EXCLUSIVE);
273321864bc5SMatthew Dillon lwkt_create(devfs_msg_core, /*args*/NULL, &td_core, NULL,
2734d2d8515bSMatthew Dillon 0, -1, "devfs_msg_core");
27351c375cd1SMatthew Dillon while (devfs_run == 0)
2736c9e9fb21SMatthew Dillon lksleep(td_core, &devfs_lock, 0, "devfsc", 0);
2737c9e9fb21SMatthew Dillon lockmgr(&devfs_lock, LK_RELEASE);
273821864bc5SMatthew Dillon
273921864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_init finished\n");
274021864bc5SMatthew Dillon }
274121864bc5SMatthew Dillon
274221864bc5SMatthew Dillon /*
274321864bc5SMatthew Dillon * Called on unload of devfs; takes care of destroying the core
274421864bc5SMatthew Dillon * and the objcaches. Also removes aliases that are no longer needed.
274521864bc5SMatthew Dillon */
274621864bc5SMatthew Dillon static void
devfs_uninit(void)274721864bc5SMatthew Dillon devfs_uninit(void)
274821864bc5SMatthew Dillon {
274921864bc5SMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG, "devfs_uninit() called\n");
275021864bc5SMatthew Dillon
275121864bc5SMatthew Dillon devfs_msg_send(DEVFS_TERMINATE_CORE, NULL);
27521c375cd1SMatthew Dillon while (devfs_run)
27531c375cd1SMatthew Dillon tsleep(td_core, 0, "devfsc", hz*10);
27541c375cd1SMatthew Dillon tsleep(td_core, 0, "devfsc", hz);
275521864bc5SMatthew Dillon
27567cbab9daSAlex Hornung devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(ops_id));
27577cbab9daSAlex Hornung
275821864bc5SMatthew Dillon /* Destroy the objcaches */
275921864bc5SMatthew Dillon objcache_destroy(devfs_msg_cache);
276021864bc5SMatthew Dillon objcache_destroy(devfs_node_cache);
276121864bc5SMatthew Dillon objcache_destroy(devfs_dev_cache);
276221864bc5SMatthew Dillon
276321864bc5SMatthew Dillon devfs_alias_reap();
276421864bc5SMatthew Dillon }
276521864bc5SMatthew Dillon
276621864bc5SMatthew Dillon /*
276721864bc5SMatthew Dillon * This is a sysctl handler to assist userland devname(3) to
276821864bc5SMatthew Dillon * find the device name for a given udev.
276921864bc5SMatthew Dillon */
277021864bc5SMatthew Dillon static int
devfs_sysctl_devname_helper(SYSCTL_HANDLER_ARGS)277121864bc5SMatthew Dillon devfs_sysctl_devname_helper(SYSCTL_HANDLER_ARGS)
277221864bc5SMatthew Dillon {
277391ffdfc5SSascha Wildner dev_t udev;
277421864bc5SMatthew Dillon cdev_t found;
277521864bc5SMatthew Dillon int error;
277621864bc5SMatthew Dillon
277791ffdfc5SSascha Wildner if ((error = SYSCTL_IN(req, &udev, sizeof(dev_t))))
277821864bc5SMatthew Dillon return (error);
277921864bc5SMatthew Dillon
2780618537cfSMatthew Dillon devfs_debug(DEVFS_DEBUG_DEBUG,
2781618537cfSMatthew Dillon "devfs sysctl, received udev: %d\n", udev);
278221864bc5SMatthew Dillon
278321864bc5SMatthew Dillon if (udev == NOUDEV)
278421864bc5SMatthew Dillon return(EINVAL);
278521864bc5SMatthew Dillon
27862ac7d105SSascha Wildner if ((found = devfs_find_device_by_devid(udev)) == NULL)
278721864bc5SMatthew Dillon return(ENOENT);
278821864bc5SMatthew Dillon
278921864bc5SMatthew Dillon return(SYSCTL_OUT(req, found->si_name, strlen(found->si_name) + 1));
279021864bc5SMatthew Dillon }
279121864bc5SMatthew Dillon
279221864bc5SMatthew Dillon
2793618537cfSMatthew Dillon SYSCTL_PROC(_kern, OID_AUTO, devname,
2794618537cfSMatthew Dillon CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_NOLOCK,
2795618537cfSMatthew Dillon NULL, 0, devfs_sysctl_devname_helper, "",
2796618537cfSMatthew Dillon "helper for devname(3)");
279721864bc5SMatthew Dillon
27983a1032a6SAlex Hornung SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "devfs");
279921864bc5SMatthew Dillon TUNABLE_INT("vfs.devfs.debug", &devfs_debug_enable);
2800bc185c5aSAlex Hornung SYSCTL_INT(_vfs_devfs, OID_AUTO, debug, CTLFLAG_RW, &devfs_debug_enable,
2801bc185c5aSAlex Hornung 0, "Enable DevFS debugging");
280221864bc5SMatthew Dillon
2803c0739b3cSMatthew Dillon SYSINIT(vfs_devfs_register, SI_SUB_DEVFS_CORE, SI_ORDER_FIRST,
2804bc185c5aSAlex Hornung devfs_init, NULL);
2805c0739b3cSMatthew Dillon SYSUNINIT(vfs_devfs_register, SI_SUB_DEVFS_CORE, SI_ORDER_ANY,
2806bc185c5aSAlex Hornung devfs_uninit, NULL);
28078312ca30SAlex Hornung
28088312ca30SAlex Hornung /*
28098312ca30SAlex Hornung * WildCmp() - compare wild string to sane string
28108312ca30SAlex Hornung *
28118312ca30SAlex Hornung * Returns 0 on success, -1 on failure.
28128312ca30SAlex Hornung */
28138312ca30SAlex Hornung static int
wildCmp(const char ** mary,int d,const char * w,const char * s)28148312ca30SAlex Hornung wildCmp(const char **mary, int d, const char *w, const char *s)
28158312ca30SAlex Hornung {
28168312ca30SAlex Hornung int i;
28178312ca30SAlex Hornung
28188312ca30SAlex Hornung /*
28198312ca30SAlex Hornung * skip fixed portion
28208312ca30SAlex Hornung */
28218312ca30SAlex Hornung for (;;) {
28228312ca30SAlex Hornung switch(*w) {
28238312ca30SAlex Hornung case '*':
28248312ca30SAlex Hornung /*
28258312ca30SAlex Hornung * optimize terminator
28268312ca30SAlex Hornung */
28278312ca30SAlex Hornung if (w[1] == 0)
28288312ca30SAlex Hornung return(0);
28298312ca30SAlex Hornung if (w[1] != '?' && w[1] != '*') {
28308312ca30SAlex Hornung /*
28318312ca30SAlex Hornung * optimize * followed by non-wild
28328312ca30SAlex Hornung */
28338312ca30SAlex Hornung for (i = 0; s + i < mary[d]; ++i) {
28348312ca30SAlex Hornung if (s[i] == w[1] && wildCmp(mary, d + 1, w + 1, s + i) == 0)
28358312ca30SAlex Hornung return(0);
28368312ca30SAlex Hornung }
28378312ca30SAlex Hornung } else {
28388312ca30SAlex Hornung /*
28398312ca30SAlex Hornung * less-optimal
28408312ca30SAlex Hornung */
28418312ca30SAlex Hornung for (i = 0; s + i < mary[d]; ++i) {
28428312ca30SAlex Hornung if (wildCmp(mary, d + 1, w + 1, s + i) == 0)
28438312ca30SAlex Hornung return(0);
28448312ca30SAlex Hornung }
28458312ca30SAlex Hornung }
28468312ca30SAlex Hornung mary[d] = s;
28478312ca30SAlex Hornung return(-1);
28488312ca30SAlex Hornung case '?':
28498312ca30SAlex Hornung if (*s == 0)
28508312ca30SAlex Hornung return(-1);
28518312ca30SAlex Hornung ++w;
28528312ca30SAlex Hornung ++s;
28538312ca30SAlex Hornung break;
28548312ca30SAlex Hornung default:
28558312ca30SAlex Hornung if (*w != *s)
28568312ca30SAlex Hornung return(-1);
28578312ca30SAlex Hornung if (*w == 0) /* terminator */
28588312ca30SAlex Hornung return(0);
28598312ca30SAlex Hornung ++w;
28608312ca30SAlex Hornung ++s;
28618312ca30SAlex Hornung break;
28628312ca30SAlex Hornung }
28638312ca30SAlex Hornung }
28648312ca30SAlex Hornung /* not reached */
28658312ca30SAlex Hornung return(-1);
28668312ca30SAlex Hornung }
28678312ca30SAlex Hornung
28688312ca30SAlex Hornung
28698312ca30SAlex Hornung /*
28708312ca30SAlex Hornung * WildCaseCmp() - compare wild string to sane string, case insensitive
28718312ca30SAlex Hornung *
28728312ca30SAlex Hornung * Returns 0 on success, -1 on failure.
28738312ca30SAlex Hornung */
28748312ca30SAlex Hornung static int
wildCaseCmp(const char ** mary,int d,const char * w,const char * s)28758312ca30SAlex Hornung wildCaseCmp(const char **mary, int d, const char *w, const char *s)
28768312ca30SAlex Hornung {
28778312ca30SAlex Hornung int i;
28788312ca30SAlex Hornung
28798312ca30SAlex Hornung /*
28808312ca30SAlex Hornung * skip fixed portion
28818312ca30SAlex Hornung */
28828312ca30SAlex Hornung for (;;) {
28838312ca30SAlex Hornung switch(*w) {
28848312ca30SAlex Hornung case '*':
28858312ca30SAlex Hornung /*
28868312ca30SAlex Hornung * optimize terminator
28878312ca30SAlex Hornung */
28888312ca30SAlex Hornung if (w[1] == 0)
28898312ca30SAlex Hornung return(0);
28908312ca30SAlex Hornung if (w[1] != '?' && w[1] != '*') {
28918312ca30SAlex Hornung /*
28928312ca30SAlex Hornung * optimize * followed by non-wild
28938312ca30SAlex Hornung */
28948312ca30SAlex Hornung for (i = 0; s + i < mary[d]; ++i) {
28958312ca30SAlex Hornung if (s[i] == w[1] && wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
28968312ca30SAlex Hornung return(0);
28978312ca30SAlex Hornung }
28988312ca30SAlex Hornung } else {
28998312ca30SAlex Hornung /*
29008312ca30SAlex Hornung * less-optimal
29018312ca30SAlex Hornung */
29028312ca30SAlex Hornung for (i = 0; s + i < mary[d]; ++i) {
29038312ca30SAlex Hornung if (wildCaseCmp(mary, d + 1, w + 1, s + i) == 0)
29048312ca30SAlex Hornung return(0);
29058312ca30SAlex Hornung }
29068312ca30SAlex Hornung }
29078312ca30SAlex Hornung mary[d] = s;
29088312ca30SAlex Hornung return(-1);
29098312ca30SAlex Hornung case '?':
29108312ca30SAlex Hornung if (*s == 0)
29118312ca30SAlex Hornung return(-1);
29128312ca30SAlex Hornung ++w;
29138312ca30SAlex Hornung ++s;
29148312ca30SAlex Hornung break;
29158312ca30SAlex Hornung default:
29168312ca30SAlex Hornung if (*w != *s) {
29178312ca30SAlex Hornung #define tolower(x) ((x >= 'A' && x <= 'Z')?(x+('a'-'A')):(x))
29188312ca30SAlex Hornung if (tolower(*w) != tolower(*s))
29198312ca30SAlex Hornung return(-1);
29208312ca30SAlex Hornung }
29218312ca30SAlex Hornung if (*w == 0) /* terminator */
29228312ca30SAlex Hornung return(0);
29238312ca30SAlex Hornung ++w;
29248312ca30SAlex Hornung ++s;
29258312ca30SAlex Hornung break;
29268312ca30SAlex Hornung }
29278312ca30SAlex Hornung }
29288312ca30SAlex Hornung /* not reached */
29298312ca30SAlex Hornung return(-1);
29308312ca30SAlex Hornung }
29318312ca30SAlex Hornung
293291c397b9SJohannes Hofmann struct cdev_privdata {
293391c397b9SJohannes Hofmann void *cdpd_data;
2934*d56bec7aSAaron LI d_priv_dtor_t *cdpd_dtr;
293591c397b9SJohannes Hofmann };
293691c397b9SJohannes Hofmann
2937f4dab6e9SSascha Wildner int
devfs_get_cdevpriv(struct file * fp,void ** datap)2938f4dab6e9SSascha Wildner devfs_get_cdevpriv(struct file *fp, void **datap)
293991c397b9SJohannes Hofmann {
294091c397b9SJohannes Hofmann int error;
294191c397b9SJohannes Hofmann
294291c397b9SJohannes Hofmann if (fp == NULL)
294391c397b9SJohannes Hofmann return(EBADF);
2944d0d1aa2fSSepherosa Ziehau
2945d0d1aa2fSSepherosa Ziehau spin_lock_shared(&fp->f_spin);
2946d0d1aa2fSSepherosa Ziehau if (fp->f_data1 == NULL) {
29476e886f9eSMatthew Dillon *datap = NULL;
29486e886f9eSMatthew Dillon error = ENOENT;
29496e886f9eSMatthew Dillon } else {
2950d0d1aa2fSSepherosa Ziehau struct cdev_privdata *p = fp->f_data1;
2951d0d1aa2fSSepherosa Ziehau
295291c397b9SJohannes Hofmann *datap = p->cdpd_data;
2953d0d1aa2fSSepherosa Ziehau error = 0;
29546e886f9eSMatthew Dillon }
2955d0d1aa2fSSepherosa Ziehau spin_unlock_shared(&fp->f_spin);
2956d0d1aa2fSSepherosa Ziehau
295791c397b9SJohannes Hofmann return (error);
295891c397b9SJohannes Hofmann }
295991c397b9SJohannes Hofmann
2960f4dab6e9SSascha Wildner int
devfs_set_cdevpriv(struct file * fp,void * priv,d_priv_dtor_t * dtr)2961*d56bec7aSAaron LI devfs_set_cdevpriv(struct file *fp, void *priv, d_priv_dtor_t *dtr)
296291c397b9SJohannes Hofmann {
296391c397b9SJohannes Hofmann struct cdev_privdata *p;
296491c397b9SJohannes Hofmann int error;
296591c397b9SJohannes Hofmann
296691c397b9SJohannes Hofmann if (fp == NULL)
296791c397b9SJohannes Hofmann return (ENOENT);
296891c397b9SJohannes Hofmann
296991c397b9SJohannes Hofmann p = kmalloc(sizeof(struct cdev_privdata), M_DEVFS, M_WAITOK);
297091c397b9SJohannes Hofmann p->cdpd_data = priv;
297191c397b9SJohannes Hofmann p->cdpd_dtr = dtr;
297291c397b9SJohannes Hofmann
297391c397b9SJohannes Hofmann spin_lock(&fp->f_spin);
297491c397b9SJohannes Hofmann if (fp->f_data1 == NULL) {
297591c397b9SJohannes Hofmann fp->f_data1 = p;
297691c397b9SJohannes Hofmann error = 0;
29776e886f9eSMatthew Dillon } else {
297891c397b9SJohannes Hofmann error = EBUSY;
29796e886f9eSMatthew Dillon }
298091c397b9SJohannes Hofmann spin_unlock(&fp->f_spin);
298191c397b9SJohannes Hofmann
298291c397b9SJohannes Hofmann if (error)
298391c397b9SJohannes Hofmann kfree(p, M_DEVFS);
298491c397b9SJohannes Hofmann
298591c397b9SJohannes Hofmann return error;
298691c397b9SJohannes Hofmann }
298791c397b9SJohannes Hofmann
2988f4dab6e9SSascha Wildner void
devfs_clear_cdevpriv(struct file * fp)2989f4dab6e9SSascha Wildner devfs_clear_cdevpriv(struct file *fp)
299091c397b9SJohannes Hofmann {
299191c397b9SJohannes Hofmann struct cdev_privdata *p;
299291c397b9SJohannes Hofmann
299391c397b9SJohannes Hofmann if (fp == NULL)
299491c397b9SJohannes Hofmann return;
299591c397b9SJohannes Hofmann
299691c397b9SJohannes Hofmann spin_lock(&fp->f_spin);
299791c397b9SJohannes Hofmann p = fp->f_data1;
299891c397b9SJohannes Hofmann fp->f_data1 = NULL;
299991c397b9SJohannes Hofmann spin_unlock(&fp->f_spin);
3000d0d1aa2fSSepherosa Ziehau
300191c397b9SJohannes Hofmann if (p != NULL) {
3002d0d1aa2fSSepherosa Ziehau p->cdpd_dtr(p->cdpd_data);
300391c397b9SJohannes Hofmann kfree(p, M_DEVFS);
300491c397b9SJohannes Hofmann }
300591c397b9SJohannes Hofmann }
300691c397b9SJohannes Hofmann
30078312ca30SAlex Hornung int
devfs_WildCmp(const char * w,const char * s)30088312ca30SAlex Hornung devfs_WildCmp(const char *w, const char *s)
30098312ca30SAlex Hornung {
30108312ca30SAlex Hornung int i;
30118312ca30SAlex Hornung int c;
30128312ca30SAlex Hornung int slen = strlen(s);
30138312ca30SAlex Hornung const char **mary;
30148312ca30SAlex Hornung
30158312ca30SAlex Hornung for (i = c = 0; w[i]; ++i) {
30168312ca30SAlex Hornung if (w[i] == '*')
30178312ca30SAlex Hornung ++c;
30188312ca30SAlex Hornung }
30198312ca30SAlex Hornung mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
30208312ca30SAlex Hornung for (i = 0; i < c; ++i)
30218312ca30SAlex Hornung mary[i] = s + slen;
30228312ca30SAlex Hornung i = wildCmp(mary, 0, w, s);
30238312ca30SAlex Hornung kfree(mary, M_DEVFS);
30248312ca30SAlex Hornung return(i);
30258312ca30SAlex Hornung }
30268312ca30SAlex Hornung
30278312ca30SAlex Hornung int
devfs_WildCaseCmp(const char * w,const char * s)30288312ca30SAlex Hornung devfs_WildCaseCmp(const char *w, const char *s)
30298312ca30SAlex Hornung {
30308312ca30SAlex Hornung int i;
30318312ca30SAlex Hornung int c;
30328312ca30SAlex Hornung int slen = strlen(s);
30338312ca30SAlex Hornung const char **mary;
30348312ca30SAlex Hornung
30358312ca30SAlex Hornung for (i = c = 0; w[i]; ++i) {
30368312ca30SAlex Hornung if (w[i] == '*')
30378312ca30SAlex Hornung ++c;
30388312ca30SAlex Hornung }
30398312ca30SAlex Hornung mary = kmalloc(sizeof(char *) * (c + 1), M_DEVFS, M_WAITOK);
30408312ca30SAlex Hornung for (i = 0; i < c; ++i)
30418312ca30SAlex Hornung mary[i] = s + slen;
30428312ca30SAlex Hornung i = wildCaseCmp(mary, 0, w, s);
30438312ca30SAlex Hornung kfree(mary, M_DEVFS);
30448312ca30SAlex Hornung return(i);
30458312ca30SAlex Hornung }
30468312ca30SAlex Hornung
3047