xref: /freebsd/sys/compat/linsysfs/linsysfs.c (revision 102adf88e6e8f83a9ab9769732d168c501f8eb6c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2006 IronPort Systems
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/ctype.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/mount.h>
35 #include <sys/sbuf.h>
36 #include <sys/smp.h>
37 #include <sys/bus.h>
38 #include <sys/pciio.h>
39 
40 #include <dev/pci/pcivar.h>
41 #include <dev/pci/pcireg.h>
42 
43 #include <compat/linux/linux_util.h>
44 #include <fs/pseudofs/pseudofs.h>
45 
46 #include <compat/linsysfs/linsysfs.h>
47 
48 MALLOC_DEFINE(M_LINSYSFS, "linsysfs", "Linsysfs structures");
49 
50 struct scsi_host_queue {
51 	TAILQ_ENTRY(scsi_host_queue) scsi_host_next;
52 	char *path;
53 	char *name;
54 };
55 
56 TAILQ_HEAD(,scsi_host_queue) scsi_host_q;
57 
58 static int host_number = 0;
59 
60 static int
61 atoi(const char *str)
62 {
63 	return (int)strtol(str, (char **)NULL, 10);
64 }
65 
66 /*
67  * Filler function for proc_name
68  */
69 static int
70 linsysfs_scsiname(PFS_FILL_ARGS)
71 {
72 	struct scsi_host_queue *scsi_host;
73 	int index;
74 
75 	if (strncmp(pn->pn_parent->pn_name, "host", 4) == 0) {
76 		index = atoi(&pn->pn_parent->pn_name[4]);
77 	} else {
78 		sbuf_printf(sb, "unknown\n");
79 		return (0);
80 	}
81 	TAILQ_FOREACH(scsi_host, &scsi_host_q, scsi_host_next) {
82 		if (index-- == 0) {
83 			sbuf_printf(sb, "%s\n", scsi_host->name);
84 			return (0);
85 		}
86 	}
87 	sbuf_printf(sb, "unknown\n");
88 	return (0);
89 }
90 
91 /*
92  * Filler function for device sym-link
93  */
94 static int
95 linsysfs_link_scsi_host(PFS_FILL_ARGS)
96 {
97 	struct scsi_host_queue *scsi_host;
98 	int index;
99 
100 	if (strncmp(pn->pn_parent->pn_name, "host", 4) == 0) {
101 		index = atoi(&pn->pn_parent->pn_name[4]);
102 	} else {
103 		sbuf_printf(sb, "unknown\n");
104 		return (0);
105 	}
106 	TAILQ_FOREACH(scsi_host, &scsi_host_q, scsi_host_next) {
107 		if (index-- == 0) {
108 			sbuf_printf(sb, "../../../devices%s", scsi_host->path);
109 			return(0);
110 		}
111 	}
112 	sbuf_printf(sb, "unknown\n");
113 	return (0);
114 }
115 
116 static int
117 linsysfs_fill_data(PFS_FILL_ARGS)
118 {
119 	sbuf_printf(sb, "%s", (char *)pn->pn_data);
120 	return (0);
121 }
122 
123 static int
124 linsysfs_fill_vendor(PFS_FILL_ARGS)
125 {
126 	sbuf_printf(sb, "0x%04x\n", pci_get_vendor((device_t)pn->pn_data));
127 	return (0);
128 }
129 
130 static int
131 linsysfs_fill_device(PFS_FILL_ARGS)
132 {
133 	sbuf_printf(sb, "0x%04x\n", pci_get_device((device_t)pn->pn_data));
134 	return (0);
135 }
136 
137 static int
138 linsysfs_fill_subvendor(PFS_FILL_ARGS)
139 {
140 	sbuf_printf(sb, "0x%04x\n", pci_get_subvendor((device_t)pn->pn_data));
141 	return (0);
142 }
143 
144 static int
145 linsysfs_fill_subdevice(PFS_FILL_ARGS)
146 {
147 	sbuf_printf(sb, "0x%04x\n", pci_get_subdevice((device_t)pn->pn_data));
148 	return (0);
149 }
150 
151 static int
152 linsysfs_fill_revid(PFS_FILL_ARGS)
153 {
154 	sbuf_printf(sb, "0x%x\n", pci_get_revid((device_t)pn->pn_data));
155 	return (0);
156 }
157 
158 static int
159 linsysfs_fill_config(PFS_FILL_ARGS)
160 {
161 	uint8_t config[48];
162 	device_t dev;
163 	uint32_t reg;
164 
165 	dev = (device_t)pn->pn_data;
166 	bzero(config, sizeof(config));
167 	reg = pci_get_vendor(dev);
168 	config[0] = reg;
169 	config[1] = reg >> 8;
170 	reg = pci_get_device(dev);
171 	config[2] = reg;
172 	config[3] = reg >> 8;
173 	reg = pci_get_revid(dev);
174 	config[8] = reg;
175 	reg = pci_get_subvendor(dev);
176 	config[44] = reg;
177 	config[45] = reg >> 8;
178 	reg = pci_get_subdevice(dev);
179 	config[46] = reg;
180 	config[47] = reg >> 8;
181 	sbuf_bcat(sb, config, sizeof(config));
182 	return (0);
183 }
184 
185 /*
186  * Filler function for PCI uevent file
187  */
188 static int
189 linsysfs_fill_uevent_pci(PFS_FILL_ARGS)
190 {
191 	device_t dev;
192 
193 	dev = (device_t)pn->pn_data;
194 	sbuf_printf(sb, "DRIVER=%s\nPCI_CLASS=%X\nPCI_ID=%04X:%04X\n"
195 	    "PCI_SUBSYS_ID=%04X:%04X\nPCI_SLOT_NAME=%04d:%02x:%02x.%x\n",
196 	    linux_driver_get_name_dev(dev), pci_get_class(dev),
197 	    pci_get_vendor(dev), pci_get_device(dev), pci_get_subvendor(dev),
198 	    pci_get_subdevice(dev), pci_get_domain(dev), pci_get_bus(dev),
199 	    pci_get_slot(dev), pci_get_function(dev));
200 	return (0);
201 }
202 
203 /* Linux DRM render nodes start at minor 128. */
204 #define DRM_RENDER_MINOR_BASE 128
205 
206 /*
207  * Filler function for drm uevent file
208  */
209 static int
210 linsysfs_fill_uevent_drm(PFS_FILL_ARGS)
211 {
212 	device_t dev;
213 	int unit;
214 
215 	dev = (device_t)pn->pn_data;
216 	unit = device_get_unit(dev);
217 	sbuf_printf(sb,
218 	    "MAJOR=226\nMINOR=%d\nDEVNAME=dri/card%d\nDEVTYPE=dri_minor\n",
219 	    unit, unit);
220 	return (0);
221 }
222 
223 static int
224 linsysfs_fill_uevent_drm_render(PFS_FILL_ARGS)
225 {
226 	device_t dev;
227 	int unit;
228 
229 	dev = (device_t)pn->pn_data;
230 	unit = device_get_unit(dev);
231 	sbuf_printf(sb, "MAJOR=226\nMINOR=%d\nDEVNAME=dri/renderD%d\n",
232 	    unit + DRM_RENDER_MINOR_BASE, unit + DRM_RENDER_MINOR_BASE);
233 	return (0);
234 }
235 
236 static char *
237 get_full_pfs_path(struct pfs_node *cur)
238 {
239 	char *temp, *path;
240 
241 	temp = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
242 	path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
243 	path[0] = '\0';
244 
245 	do {
246 		snprintf(temp, MAXPATHLEN, "%s/%s", cur->pn_name, path);
247 		strlcpy(path, temp, MAXPATHLEN);
248 		cur = cur->pn_parent;
249 	} while (cur->pn_parent != NULL);
250 
251 	path[strlen(path) - 1] = '\0'; /* remove extra slash */
252 	free(temp, M_TEMP);
253 	return (path);
254 }
255 
256 /*
257  * Filler function for symlink from drm char device to PCI device
258  */
259 static int
260 linsysfs_fill_vgapci(PFS_FILL_ARGS)
261 {
262 	char *path;
263 
264 	path = get_full_pfs_path((struct pfs_node*)pn->pn_data);
265 	sbuf_printf(sb, "../../../%s", path);
266 	free(path, M_TEMP);
267 	return (0);
268 }
269 
270 #undef PCI_DEV
271 #define PCI_DEV "pci"
272 #define DRMN_DEV "drmn"
273 static int
274 linsysfs_run_bus(device_t dev, struct pfs_node *dir, struct pfs_node *scsi,
275     struct pfs_node *chardev, struct pfs_node *drm, char *path, char *prefix)
276 {
277 	struct scsi_host_queue *scsi_host;
278 	struct pfs_node *sub_dir, *cur_file, *drm_dir, *drm_minor;
279 	int i, nchildren, error;
280 	device_t *children, parent;
281 	devclass_t devclass;
282 	const char *name = NULL;
283 	struct pci_devinfo *dinfo;
284 	char *device, *host, *new_path, *devname;
285 
286 	children = NULL;
287 	device = host = NULL;
288 	new_path = path;
289 	devname = malloc(16, M_TEMP, M_WAITOK);
290 
291 	parent = device_get_parent(dev);
292 	if (parent) {
293 		devclass = device_get_devclass(parent);
294 		if (devclass != NULL)
295 			name = devclass_get_name(devclass);
296 		if (name && strcmp(name, PCI_DEV) == 0) {
297 			dinfo = device_get_ivars(dev);
298 			if (dinfo) {
299 				device = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
300 				new_path = malloc(MAXPATHLEN, M_TEMP,
301 				    M_WAITOK);
302 				new_path[0] = '\000';
303 				strcpy(new_path, path);
304 				host = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
305 				device[0] = '\000';
306 				sprintf(device, "%s:%02x:%02x.%x",
307 				    prefix,
308 				    dinfo->cfg.bus,
309 				    dinfo->cfg.slot,
310 				    dinfo->cfg.func);
311 				strcat(new_path, "/");
312 				strcat(new_path, device);
313 				error = pfs_create_dir(dir, &dir, device,
314 				    NULL, NULL, NULL, 0);
315 				if (error != 0)
316 					goto out;
317 				pfs_create_dir(dir, &dir, device, NULL, NULL,
318 				    NULL, 0);
319 				pfs_create_file(dir, &cur_file, "vendor",
320 				    &linsysfs_fill_vendor, NULL, NULL, NULL,
321 				    PFS_RD);
322 				cur_file->pn_data = (void*)dev;
323 				pfs_create_file(dir, &cur_file, "device",
324 				    &linsysfs_fill_device, NULL, NULL, NULL,
325 				    PFS_RD);
326 				cur_file->pn_data = (void*)dev;
327 				pfs_create_file(dir, &cur_file,
328 				    "subsystem_vendor",
329 				    &linsysfs_fill_subvendor, NULL, NULL, NULL,
330 				    PFS_RD);
331 				cur_file->pn_data = (void*)dev;
332 				pfs_create_file(dir, &cur_file,
333 				    "subsystem_device",
334 				    &linsysfs_fill_subdevice, NULL, NULL, NULL,
335 				    PFS_RD);
336 				cur_file->pn_data = (void*)dev;
337 				pfs_create_file(dir, &cur_file, "revision",
338 				    &linsysfs_fill_revid, NULL, NULL, NULL,
339 				    PFS_RD);
340 				cur_file->pn_data = (void*)dev;
341 				pfs_create_file(dir, &cur_file, "config",
342 				    &linsysfs_fill_config, NULL, NULL, NULL,
343 				    PFS_RD);
344 				cur_file->pn_data = (void*)dev;
345 				pfs_create_file(dir, &cur_file, "uevent",
346 				    &linsysfs_fill_uevent_pci, NULL, NULL, NULL,
347 				    PFS_RD);
348 				cur_file->pn_data = (void*)dev;
349 				pfs_create_link(dir, &cur_file, "subsystem",
350 				    &linsysfs_fill_data, NULL, NULL, NULL, 0);
351 				/* libdrm just checks that the link ends in "/pci" */
352 				cur_file->pn_data = "/sys/bus/pci";
353 
354 				if (dinfo->cfg.baseclass == PCIC_STORAGE) {
355 					/* DJA only make this if needed */
356 					sprintf(host, "host%d", host_number++);
357 					strcat(new_path, "/");
358 					strcat(new_path, host);
359 					pfs_create_dir(dir, NULL, host, NULL,
360 					    NULL, NULL, 0);
361 					scsi_host = malloc(sizeof(
362 					    struct scsi_host_queue),
363 					    M_DEVBUF, M_WAITOK);
364 					scsi_host->path = malloc(
365 					    strlen(new_path) + 1,
366 					    M_DEVBUF, M_WAITOK);
367 					scsi_host->path[0] = '\000';
368 					bcopy(new_path, scsi_host->path,
369 					    strlen(new_path) + 1);
370 					scsi_host->name = "unknown";
371 
372 					pfs_create_dir(scsi, &sub_dir, host,
373 					    NULL, NULL, NULL, 0);
374 					pfs_create_link(sub_dir, NULL, "device",
375 					    &linsysfs_link_scsi_host, NULL,
376 					    NULL, NULL, 0);
377 					pfs_create_file(sub_dir, NULL,
378 					    "proc_name", &linsysfs_scsiname,
379 					    NULL, NULL, NULL, PFS_RD);
380 					scsi_host->name
381 					    = linux_driver_get_name_dev(dev);
382 					TAILQ_INSERT_TAIL(&scsi_host_q,
383 					    scsi_host, scsi_host_next);
384 				}
385 			}
386 		}
387 
388 		devclass = device_get_devclass(dev);
389 		if (devclass != NULL)
390 			name = devclass_get_name(devclass);
391 		else
392 			name = NULL;
393 		if (name != NULL && strcmp(name, DRMN_DEV) == 0 &&
394 		    device_get_unit(dev) >= 0) {
395 			dinfo = device_get_ivars(parent);
396 			if (dinfo != NULL && dinfo->cfg.baseclass == PCIC_DISPLAY) {
397 				pfs_create_dir(dir, &drm_dir, "drm", NULL, NULL,
398 				    NULL, 0);
399 				sprintf(devname, "226:%d",
400 				    device_get_unit(dev));
401 				pfs_create_dir(chardev, &sub_dir, devname, NULL,
402 				    NULL, NULL, 0);
403 				pfs_create_link(sub_dir, &cur_file, "device",
404 				    &linsysfs_fill_vgapci, NULL, NULL, NULL,
405 				    PFS_RD);
406 				cur_file->pn_data = (void*)dir;
407 				pfs_create_file(sub_dir, &cur_file, "uevent",
408 				    &linsysfs_fill_uevent_drm, NULL, NULL, NULL,
409 				    PFS_RD);
410 				cur_file->pn_data = (void*)dev;
411 				sprintf(devname, "card%d",
412 				    device_get_unit(dev));
413 				pfs_create_dir(drm, &sub_dir, devname, NULL,
414 				    NULL, NULL, 0);
415 				pfs_create_link(sub_dir, &cur_file, "device",
416 				    &linsysfs_fill_vgapci, NULL, NULL, NULL,
417 				    PFS_RD);
418 				cur_file->pn_data = (void*)dir;
419 
420 				pfs_create_dir(drm_dir, &drm_minor, devname,
421 				    NULL, NULL, NULL, 0);
422 				pfs_create_file(drm_minor, &cur_file, "uevent",
423 				    &linsysfs_fill_uevent_drm, NULL, NULL, NULL,
424 				    PFS_RD);
425 				cur_file->pn_data = (void *)dev;
426 
427 				sprintf(devname, "226:%d",
428 				    device_get_unit(dev) +
429 					DRM_RENDER_MINOR_BASE);
430 				pfs_create_dir(chardev, &sub_dir, devname, NULL,
431 				    NULL, NULL, 0);
432 				pfs_create_link(sub_dir, &cur_file, "device",
433 				    &linsysfs_fill_vgapci, NULL, NULL, NULL,
434 				    PFS_RD);
435 				cur_file->pn_data = (void *)dir;
436 				pfs_create_file(sub_dir, &cur_file, "uevent",
437 				    &linsysfs_fill_uevent_drm_render, NULL,
438 				    NULL, NULL, PFS_RD);
439 				cur_file->pn_data = (void *)dev;
440 
441 				sprintf(devname, "renderD%d",
442 				    device_get_unit(dev) +
443 					DRM_RENDER_MINOR_BASE);
444 				pfs_create_dir(drm, &sub_dir, devname, NULL,
445 				    NULL, NULL, 0);
446 				pfs_create_link(sub_dir, &cur_file, "device",
447 				    &linsysfs_fill_vgapci, NULL, NULL, NULL,
448 				    PFS_RD);
449 				cur_file->pn_data = (void *)dir;
450 
451 				pfs_create_dir(drm_dir, &drm_minor, devname,
452 				    NULL, NULL, NULL, 0);
453 				pfs_create_file(drm_minor, &cur_file, "uevent",
454 				    &linsysfs_fill_uevent_drm_render, NULL,
455 				    NULL, NULL, PFS_RD);
456 				cur_file->pn_data = (void *)dev;
457 			}
458 		}
459 	}
460 
461 	error = device_get_children(dev, &children, &nchildren);
462 	if (error == 0) {
463 		for (i = 0; i < nchildren; i++) {
464 			if (children[i]) {
465 				error = linsysfs_run_bus(children[i], dir, scsi,
466 				    chardev, drm, new_path, prefix);
467 				if (error != 0) {
468 					printf(
469 					    "linsysfs_run_bus: %s omitted from sysfs tree, error %d\n",
470 					    device_get_nameunit(children[i]),
471 					    error);
472 				}
473 			}
474 		}
475 
476 		/*
477 		 * We override the error to avoid cascading failures; the
478 		 * innermost device that failed in a tree is probably the most
479 		 * significant one for diagnostics, its parents would be noise.
480 		 */
481 		error = 0;
482 	}
483 
484 out:
485 	free(host, M_TEMP);
486 	free(device, M_TEMP);
487 	if (children != NULL)
488 		free(children, M_TEMP);
489 	if (new_path != path)
490 		free(new_path, M_TEMP);
491 	free(devname, M_TEMP);
492 
493 	return (error);
494 }
495 
496 /*
497  * Filler function for sys/devices/system/cpu/{online,possible,present}
498  */
499 static int
500 linsysfs_cpuonline(PFS_FILL_ARGS)
501 {
502 
503 	sbuf_printf(sb, "%d-%d\n", CPU_FIRST(), mp_maxid);
504 	return (0);
505 }
506 
507 /*
508  * Filler function for sys/devices/system/cpu/cpuX/online
509  */
510 static int
511 linsysfs_cpuxonline(PFS_FILL_ARGS)
512 {
513 
514 	sbuf_printf(sb, "1\n");
515 	return (0);
516 }
517 
518 static void
519 linsysfs_listcpus(struct pfs_node *dir)
520 {
521 	struct pfs_node *cpu;
522 	char *name;
523 	int i, count, len;
524 
525 	len = 1;
526 	count = mp_maxcpus;
527 	while (count > 10) {
528 		count /= 10;
529 		len++;
530 	}
531 	len += sizeof("cpu");
532 	name = malloc(len, M_TEMP, M_WAITOK);
533 
534 	for (i = 0; i < mp_ncpus; ++i) {
535 		/* /sys/devices/system/cpu/cpuX */
536 		sprintf(name, "cpu%d", i);
537 		pfs_create_dir(dir, &cpu, name, NULL, NULL, NULL, 0);
538 
539 		pfs_create_file(cpu, NULL, "online", &linsysfs_cpuxonline, NULL,
540 		    NULL, NULL, PFS_RD);
541 	}
542 	free(name, M_TEMP);
543 }
544 
545 /*
546  * Constructor
547  */
548 static int
549 linsysfs_init(PFS_INIT_ARGS)
550 {
551 	struct pfs_node *root;
552 	struct pfs_node *class;
553 	struct pfs_node *dir, *sys, *cpu;
554 	struct pfs_node *drm;
555 	struct pfs_node *pci;
556 	struct pfs_node *scsi;
557 	struct pfs_node *devdir, *chardev;
558 	struct pfs_node *kernel;
559 	devclass_t devclass;
560 	device_t dev;
561 
562 	TAILQ_INIT(&scsi_host_q);
563 
564 	root = pi->pi_root;
565 
566 	/* /sys/bus/... */
567 	pfs_create_dir(root, &dir, "bus", NULL, NULL, NULL, 0);
568 
569 	/* /sys/class/... */
570 	pfs_create_dir(root, &class, "class", NULL, NULL, NULL, 0);
571 	pfs_create_dir(class, &scsi, "scsi_host", NULL, NULL, NULL, 0);
572 	pfs_create_dir(class, &drm, "drm", NULL, NULL, NULL, 0);
573 	pfs_create_dir(class, NULL, "power_supply", NULL, NULL, NULL, 0);
574 
575 	/* /sys/class/net/.. */
576 	pfs_create_dir(class, &net, "net", NULL, NULL, NULL, 0);
577 
578 	/* /sys/dev/... */
579 	pfs_create_dir(root, &devdir, "dev", NULL, NULL, NULL, 0);
580 	pfs_create_dir(devdir, &chardev, "char", NULL, NULL, NULL, 0);
581 
582 	/* /sys/devices/... */
583 	pfs_create_dir(root, &dir, "devices", NULL, NULL, NULL, 0);
584 	pfs_create_dir(dir, &pci, "pci0000:00", NULL, NULL, NULL, 0);
585 
586 	devclass = devclass_find("root");
587 	if (devclass == NULL) {
588 		return (0);
589 	}
590 
591 	/*
592 	 * This assumes that the root node is unlikely to error out in
593 	 * linsysfs_run_bus, which may or may not be true.
594 	 */
595 	dev = devclass_get_device(devclass, 0);
596 	linsysfs_run_bus(dev, pci, scsi, chardev, drm, "/pci0000:00", "0000");
597 
598 	/* /sys/devices/system */
599 	pfs_create_dir(dir, &sys, "system", NULL, NULL, NULL, 0);
600 
601 	/* /sys/devices/system/cpu */
602 	pfs_create_dir(sys, &cpu, "cpu", NULL, NULL, NULL, 0);
603 
604 	pfs_create_file(cpu, NULL, "online", &linsysfs_cpuonline, NULL, NULL,
605 	    NULL, PFS_RD);
606 	pfs_create_file(cpu, NULL, "possible", &linsysfs_cpuonline, NULL, NULL,
607 	    NULL, PFS_RD);
608 	pfs_create_file(cpu, NULL, "present", &linsysfs_cpuonline, NULL, NULL,
609 	    NULL, PFS_RD);
610 
611 	linsysfs_listcpus(cpu);
612 
613 	/* /sys/kernel */
614 	pfs_create_dir(root, &kernel, "kernel", NULL, NULL, NULL, 0);
615 	/* /sys/kernel/debug, mountpoint for lindebugfs. */
616 	pfs_create_dir(kernel, NULL, "debug", NULL, NULL, NULL, 0);
617 
618 	linsysfs_net_init();
619 
620 	return (0);
621 }
622 
623 /*
624  * Destructor
625  */
626 static int
627 linsysfs_uninit(PFS_INIT_ARGS)
628 {
629 	struct scsi_host_queue *scsi_host, *scsi_host_tmp;
630 
631 	TAILQ_FOREACH_SAFE(scsi_host, &scsi_host_q, scsi_host_next,
632 	    scsi_host_tmp) {
633 		TAILQ_REMOVE(&scsi_host_q, scsi_host, scsi_host_next);
634 		free(scsi_host->path, M_TEMP);
635 		free(scsi_host, M_TEMP);
636 	}
637 
638 	linsysfs_net_uninit();
639 
640 	return (0);
641 }
642 
643 PSEUDOFS(linsysfs, 1, VFCF_JAIL);
644 #if defined(__aarch64__) || defined(__amd64__)
645 MODULE_DEPEND(linsysfs, linux_common, 1, 1, 1);
646 #else
647 MODULE_DEPEND(linsysfs, linux, 1, 1, 1);
648 #endif
649