xref: /freebsd/sys/fs/devfs/devfs_devs.c (revision 8fa113e5fc65fe6abc757f0089f477a87ee4d185)
1 #define DEBUG 1
2 /*
3  * Copyright (c) 2000
4  *	Poul-Henning Kamp.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Neither the name of the University nor the names of its contributors
12  *    may be used to endorse or promote products derived from this software
13  *    without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
28  *
29  * $FreeBSD$
30  */
31 
32 #include "opt_devfs.h"
33 #ifndef NODEVFS
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/dirent.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/proc.h>
43 #include <sys/sysctl.h>
44 #include <sys/vnode.h>
45 
46 #include <machine/atomic.h>
47 
48 #include <fs/devfs/devfs.h>
49 
50 static dev_t devfs_inot[NDEVFSINO];
51 static dev_t *devfs_overflow;
52 static int devfs_ref[NDEVFSINO];
53 static int *devfs_refoverflow;
54 static int devfs_nextino = 3;
55 static int devfs_numino;
56 static int devfs_topino;
57 static int devfs_noverflowwant = NDEVFSOVERFLOW;
58 static int devfs_noverflow;
59 static unsigned devfs_generation;
60 
61 static void devfs_attemptoverflow(int insist);
62 static struct devfs_dirent *devfs_find (struct devfs_dirent *dd, const char *name, int namelen);
63 
64 SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
65 SYSCTL_UINT(_vfs_devfs, OID_AUTO, noverflow, CTLFLAG_RW,
66 	&devfs_noverflowwant, 0, "Size of DEVFS overflow table");
67 SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
68 	&devfs_generation, 0, "DEVFS generation number");
69 SYSCTL_UINT(_vfs_devfs, OID_AUTO, inodes, CTLFLAG_RD,
70 	&devfs_numino, 0, "DEVFS inodes");
71 SYSCTL_UINT(_vfs_devfs, OID_AUTO, topinode, CTLFLAG_RD,
72 	&devfs_topino, 0, "DEVFS highest inode#");
73 
74 static int *
75 devfs_itor(int inode)
76 {
77 	if (inode < NDEVFSINO)
78 		return (&devfs_ref[inode]);
79 	else if (inode < NDEVFSINO + devfs_noverflow)
80 		return (&devfs_refoverflow[inode - NDEVFSINO]);
81 	else
82 		panic ("YRK!");
83 }
84 
85 static void
86 devfs_dropref(int inode)
87 {
88 	int *ip;
89 
90 	ip = devfs_itor(inode);
91 	atomic_add_int(ip, -1);
92 }
93 
94 static int
95 devfs_getref(int inode)
96 {
97 	int *ip, i, j;
98 	dev_t *dp;
99 
100 	ip = devfs_itor(inode);
101 	dp = devfs_itod(inode);
102 	for (;;) {
103 		i = *ip;
104 		j = i + 1;
105 		if (!atomic_cmpset_int(ip, i, j))
106 			continue;
107 		if (*dp != NULL)
108 			return (1);
109 		atomic_add_int(ip, -1);
110 		return(0);
111 	}
112 }
113 
114 struct devfs_dirent **
115 devfs_itode (struct devfs_mount *dm, int inode)
116 {
117 
118 	if (inode < NDEVFSINO)
119 		return (&dm->dm_dirent[inode]);
120 	if (devfs_overflow == NULL)
121 		return (NULL);
122 	if (inode < NDEVFSINO + devfs_noverflow)
123 		return (&dm->dm_overflow[inode - NDEVFSINO]);
124 	return (NULL);
125 }
126 
127 dev_t *
128 devfs_itod (int inode)
129 {
130 
131 	if (inode < NDEVFSINO)
132 		return (&devfs_inot[inode]);
133 	if (devfs_overflow == NULL)
134 		return (NULL);
135 	if (inode < NDEVFSINO + devfs_noverflow)
136 		return (&devfs_overflow[inode - NDEVFSINO]);
137 	return (NULL);
138 }
139 
140 static void
141 devfs_attemptoverflow(int insist)
142 {
143 	dev_t **ot;
144 	int *or;
145 	int n, nb;
146 
147 	/* Check if somebody beat us to it */
148 	if (devfs_overflow != NULL)
149 		return;
150 	ot = NULL;
151 	or = NULL;
152 	n = devfs_noverflowwant;
153 	nb = sizeof (struct dev_t *) * n;
154 	MALLOC(ot, dev_t **, nb, M_DEVFS, (insist ? M_WAITOK : M_NOWAIT) | M_ZERO);
155 	if (ot == NULL)
156 		goto bail;
157 	nb = sizeof (int) * n;
158 	MALLOC(or, int *, nb, M_DEVFS, (insist ? M_WAITOK : M_NOWAIT) | M_ZERO);
159 	if (or == NULL)
160 		goto bail;
161 	if (!atomic_cmpset_ptr(&devfs_overflow, NULL, ot))
162 		goto bail;
163 	devfs_refoverflow = or;
164 	devfs_noverflow = n;
165 	printf("DEVFS Overflow table with %d entries allocated when %d in use\n", n, devfs_numino);
166 	return;
167 
168 bail:
169 	/* Somebody beat us to it, or something went wrong. */
170 	if (ot != NULL)
171 		FREE(ot, M_DEVFS);
172 	if (or != NULL)
173 		FREE(or, M_DEVFS);
174 	return;
175 }
176 
177 static struct devfs_dirent *
178 devfs_find(struct devfs_dirent *dd, const char *name, int namelen)
179 {
180 	struct devfs_dirent *de;
181 
182 	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
183 		if (namelen != de->de_dirent->d_namlen)
184 			continue;
185 		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
186 			continue;
187 		break;
188 	}
189 	return (de);
190 }
191 
192 struct devfs_dirent *
193 devfs_newdirent(char *name, int namelen)
194 {
195 	int i;
196 	struct devfs_dirent *de;
197 	struct dirent d;
198 
199 	d.d_namlen = namelen;
200 	i = sizeof (*de) + GENERIC_DIRSIZ(&d);
201 	MALLOC(de, struct devfs_dirent *, i, M_DEVFS, M_WAITOK | M_ZERO);
202 	de->de_dirent = (struct dirent *)(de + 1);
203 	de->de_dirent->d_namlen = namelen;
204 	de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
205 	bcopy(name, de->de_dirent->d_name, namelen);
206 	de->de_dirent->d_name[namelen] = '\0';
207 	vfs_timestamp(&de->de_ctime);
208 	de->de_mtime = de->de_atime = de->de_ctime;
209 	de->de_links = 1;
210 	return (de);
211 }
212 
213 struct devfs_dirent *
214 devfs_vmkdir(char *name, int namelen, struct devfs_dirent *dotdot)
215 {
216 	struct devfs_dirent *dd;
217 	struct devfs_dirent *de;
218 
219 	dd = devfs_newdirent(name, namelen);
220 
221 	TAILQ_INIT(&dd->de_dlist);
222 
223 	dd->de_dirent->d_type = DT_DIR;
224 	dd->de_mode = 0555;
225 	dd->de_links = 2;
226 	dd->de_dir = dd;
227 
228 	de = devfs_newdirent(".", 1);
229 	de->de_dirent->d_type = DT_DIR;
230 	de->de_dir = dd;
231 	de->de_flags |= DE_DOT;
232 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
233 
234 	de = devfs_newdirent("..", 2);
235 	de->de_dirent->d_type = DT_DIR;
236 	if (dotdot == NULL)
237 		de->de_dir = dd;
238 	else
239 		de->de_dir = dotdot;
240 	de->de_flags |= DE_DOTDOT;
241 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
242 
243 	return (dd);
244 }
245 
246 static void
247 devfs_delete(struct devfs_dirent *dd, struct devfs_dirent *de)
248 {
249 
250 	if (de->de_symlink) {
251 		FREE(de->de_symlink, M_DEVFS);
252 		de->de_symlink = NULL;
253 	}
254 	if (de->de_vnode)
255 		de->de_vnode->v_data = NULL;
256 	TAILQ_REMOVE(&dd->de_dlist, de, de_list);
257 	FREE(de, M_DEVFS);
258 }
259 
260 void
261 devfs_purge(struct devfs_dirent *dd)
262 {
263 	struct devfs_dirent *de;
264 
265 	for (;;) {
266 		de = TAILQ_FIRST(&dd->de_dlist);
267 		if (de == NULL)
268 			break;
269 		devfs_delete(dd, de);
270 	}
271 	FREE(dd, M_DEVFS);
272 }
273 
274 
275 int
276 devfs_populate(struct devfs_mount *dm)
277 {
278 	int i, j;
279 	dev_t dev, pdev;
280 	struct devfs_dirent *dd;
281 	struct devfs_dirent *de, **dep;
282 	char *q, *s;
283 
284 	if (dm->dm_generation == devfs_generation)
285 		return (0);
286 	lockmgr(&dm->dm_lock, LK_UPGRADE, 0, curthread);
287 	if (devfs_noverflow && dm->dm_overflow == NULL) {
288 		i = devfs_noverflow * sizeof (struct devfs_dirent *);
289 		MALLOC(dm->dm_overflow, struct devfs_dirent **, i,
290 			M_DEVFS, M_WAITOK | M_ZERO);
291 	}
292 	while (dm->dm_generation != devfs_generation) {
293 		dm->dm_generation = devfs_generation;
294 		for (i = 0; i <= devfs_topino; i++) {
295 			dev = *devfs_itod(i);
296 			dep = devfs_itode(dm, i);
297 			de = *dep;
298 			if (dev == NULL && de == DE_DELETED) {
299 				*dep = NULL;
300 				continue;
301 			}
302 			if (dev == NULL && de != NULL) {
303 				dd = de->de_dir;
304 				*dep = NULL;
305 				TAILQ_REMOVE(&dd->de_dlist, de, de_list);
306 				if (de->de_vnode)
307 					de->de_vnode->v_data = NULL;
308 				FREE(de, M_DEVFS);
309 				devfs_dropref(i);
310 				continue;
311 			}
312 			if (dev == NULL)
313 				continue;
314 			if (de != NULL)
315 				continue;
316 			if (!devfs_getref(i))
317 				continue;
318 			dd = dm->dm_basedir;
319 			s = dev->si_name;
320 			for (;;) {
321 				for (q = s; *q != '/' && *q != '\0'; q++)
322 					continue;
323 				if (*q != '/')
324 					break;
325 				de = devfs_find(dd, s, q - s);
326 				if (de == NULL) {
327 					de = devfs_vmkdir(s, q - s, dd);
328 					de->de_inode = dm->dm_inode++;
329 					TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
330 					dd->de_links++;
331 				}
332 				s = q + 1;
333 				dd = de;
334 			}
335 			de = devfs_newdirent(s, q - s);
336 			if (dev->si_flags & SI_ALIAS) {
337 				de->de_inode = dm->dm_inode++;
338 				de->de_uid = 0;
339 				de->de_gid = 0;
340 				de->de_mode = 0755;
341 				de->de_dirent->d_type = DT_LNK;
342 				pdev = dev->si_parent;
343 				j = strlen(pdev->si_name) + 1;
344 				MALLOC(de->de_symlink, char *, j, M_DEVFS, M_WAITOK);
345 				bcopy(pdev->si_name, de->de_symlink, j);
346 			} else {
347 				de->de_inode = i;
348 				de->de_uid = dev->si_uid;
349 				de->de_gid = dev->si_gid;
350 				de->de_mode = dev->si_mode;
351 				de->de_dirent->d_type = DT_CHR;
352 			}
353 			*dep = de;
354 			de->de_dir = dd;
355 			TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
356 #if 0
357 			printf("Add ino%d %s\n", i, dev->si_name);
358 #endif
359 		}
360 	}
361 	lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, curthread);
362 	return (0);
363 }
364 
365 static void
366 devfs_create(dev_t dev)
367 {
368 	int ino, i, *ip;
369 	dev_t *dp;
370 
371 	for (;;) {
372 		/* Grab the next inode number */
373 		ino = devfs_nextino;
374 		i = ino + 1;
375 		/* wrap around when we reach the end */
376 		if (i >= NDEVFSINO + devfs_noverflow)
377 			i = 3;
378 		if (!atomic_cmpset_int(&devfs_nextino, ino, i))
379 			continue;
380 
381 		/* see if it was occupied */
382 		dp = devfs_itod(ino);
383 		if (dp == NULL)
384 			Debugger("dp == NULL\n");
385 		if (*dp != NULL)
386 			continue;
387 		ip = devfs_itor(ino);
388 		if (ip == NULL)
389 			Debugger("ip == NULL\n");
390 		if (*ip != 0)
391 			continue;
392 
393 		if (!atomic_cmpset_ptr(dp, NULL, dev))
394 			continue;
395 
396 		dev->si_inode = ino;
397 		for (;;) {
398 			i = devfs_topino;
399 			if (i >= ino)
400 				break;
401 			if (atomic_cmpset_int(&devfs_topino, i, ino))
402 				break;
403 			printf("failed topino %d %d\n", i, ino);
404 		}
405 		break;
406 	}
407 
408 	atomic_add_int(&devfs_numino, 1);
409 	atomic_add_int(&devfs_generation, 1);
410 	if (devfs_overflow == NULL && devfs_numino + 100 > NDEVFSINO)
411 		devfs_attemptoverflow(0);
412 }
413 
414 static void
415 devfs_destroy(dev_t dev)
416 {
417 	int ino, i;
418 
419 	ino = dev->si_inode;
420 	dev->si_inode = 0;
421 	if (ino == 0)
422 		return;
423 	if (atomic_cmpset_ptr(devfs_itod(ino), dev, NULL)) {
424 		atomic_add_int(&devfs_generation, 1);
425 		atomic_add_int(&devfs_numino, -1);
426 		i = devfs_nextino;
427 		if (ino < i)
428 			atomic_cmpset_int(&devfs_nextino, i, ino);
429 	}
430 }
431 
432 devfs_create_t *devfs_create_hook = devfs_create;
433 devfs_destroy_t *devfs_destroy_hook = devfs_destroy;
434 int devfs_present = 1;
435 #endif
436