mdconfig.c revision 78526
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/sbin/mdconfig/mdconfig.c 78526 2001-06-21 02:10:26Z dd $
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <fcntl.h>
16#include <unistd.h>
17#include <string.h>
18#include <err.h>
19#include <sys/ioctl.h>
20#include <sys/param.h>
21#include <sys/module.h>
22#include <sys/linker.h>
23#include <sys/mdioctl.h>
24#include <sys/sysctl.h>
25#include <sys/queue.h>
26
27int	 list(const int);
28int	 query(const int, const int);
29
30struct md_ioctl mdio;
31
32enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
33
34void mdmaybeload(void);
35void usage(void);
36
37void
38usage()
39{
40	fprintf(stderr, "Usage:\n");
41	fprintf(stderr, "\tmdconfig -a -t type [-o [no]option]... [ -f file] [-s size] [-u unit]\n");
42	fprintf(stderr, "\tmdconfig -d -u unit\n");
43	fprintf(stderr, "\tmdconfig -l [-u unit]\n");
44	fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
45	fprintf(stderr, "\t\toption = {cluster, compress, reserve}\n");
46	fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%dk (kB), %%dm (MB) or %%dg (GB)\n");
47	exit(1);
48}
49
50int
51main(int argc, char **argv)
52{
53	int ch, fd, i;
54	char *p;
55	int cmdline = 0;
56
57	for (;;) {
58		ch = getopt(argc, argv, "ab:df:lo:s:t:u:");
59		if (ch == -1)
60			break;
61		switch (ch) {
62		case 'a':
63			if (cmdline != 0)
64				usage();
65			action = ATTACH;
66			cmdline = 1;
67			break;
68		case 'd':
69			if (cmdline != 0)
70				usage();
71			action = DETACH;
72			mdio.md_options = MD_AUTOUNIT;
73			cmdline = 3;
74			break;
75		case 'l':
76			if (cmdline != 0)
77				usage();
78			action = LIST;
79			mdio.md_options = MD_AUTOUNIT;
80			cmdline = 3;
81			break;
82		case 't':
83			if (cmdline != 1)
84				usage();
85			if (!strcmp(optarg, "malloc")) {
86				mdio.md_type = MD_MALLOC;
87				mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
88			} else if (!strcmp(optarg, "preload")) {
89				mdio.md_type = MD_PRELOAD;
90				mdio.md_options = 0;
91			} else if (!strcmp(optarg, "vnode")) {
92				mdio.md_type = MD_VNODE;
93				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
94			} else if (!strcmp(optarg, "swap")) {
95				mdio.md_type = MD_SWAP;
96				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
97			} else {
98				usage();
99			}
100			cmdline=2;
101			break;
102		case 'f':
103			if (cmdline != 1 && cmdline != 2)
104				usage();
105			if (cmdline == 1) {
106				/* Imply ``-t vnode'' */
107				mdio.md_type = MD_VNODE;
108				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
109			}
110			mdio.md_file = optarg;
111			break;
112		case 'o':
113			if (cmdline != 2)
114				usage();
115			if (!strcmp(optarg, "cluster"))
116				mdio.md_options |= MD_CLUSTER;
117			else if (!strcmp(optarg, "nocluster"))
118				mdio.md_options &= ~MD_CLUSTER;
119			else if (!strcmp(optarg, "compress"))
120				mdio.md_options |= MD_COMPRESS;
121			else if (!strcmp(optarg, "nocompress"))
122				mdio.md_options &= ~MD_COMPRESS;
123			else if (!strcmp(optarg, "reserve"))
124				mdio.md_options |= MD_RESERVE;
125			else if (!strcmp(optarg, "noreserve"))
126				mdio.md_options &= ~MD_RESERVE;
127			else
128				errx(1, "Unknown option.");
129			break;
130		case 's':
131			if (cmdline != 2)
132				usage();
133			mdio.md_size = strtoul(optarg, &p, 0);
134			if (p == NULL || *p == '\0')
135				;
136			else if (*p == 'k' || *p == 'K')
137				mdio.md_size *= (1024 / DEV_BSIZE);
138			else if (*p == 'm' || *p == 'M')
139				mdio.md_size *= (1024 * 1024 / DEV_BSIZE);
140			else if (*p == 'g' || *p == 'G')
141				mdio.md_size *= (1024 * 1024 * 1024 / DEV_BSIZE);
142			else
143				errx(1, "Unknown suffix on -s argument");
144			break;
145		case 'u':
146			if (cmdline != 2 && cmdline != 3)
147				usage();
148			if (!strncmp(optarg, "/dev/", 5))
149				optarg += 5;
150			if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
151				optarg += sizeof(MD_NAME) - 1;
152			mdio.md_unit = strtoul(optarg, &p, 0);
153			if ((unsigned)mdio.md_unit == ULONG_MAX || *p != '\0')
154				errx(1, "bad unit: %s", optarg);
155			mdio.md_options &= ~MD_AUTOUNIT;
156			break;
157		default:
158			usage();
159		}
160	}
161
162	mdmaybeload();
163	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
164	if (fd < 0)
165		err(1, "open(/dev/%s)", MDCTL_NAME);
166	if (cmdline == 2
167	    && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
168		if (mdio.md_size == 0)
169			errx(1, "must specify -s for -t malloc or -t swap");
170	if (action == LIST) {
171		if (mdio.md_options & MD_AUTOUNIT)
172			list(fd);
173		else
174			query(fd, mdio.md_unit);
175	} else if (action == ATTACH) {
176		i = ioctl(fd, MDIOCATTACH, &mdio);
177		if (i < 0)
178			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
179		if (mdio.md_options & MD_AUTOUNIT)
180			printf("%s%d\n", MD_NAME, mdio.md_unit);
181	} else if (action == DETACH) {
182		if (mdio.md_options & MD_AUTOUNIT)
183			usage();
184		i = ioctl(fd, MDIOCDETACH, &mdio);
185		if (i < 0)
186			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
187	} else
188		usage();
189	close (fd);
190	return (0);
191}
192
193struct dl {
194	int		unit;
195	SLIST_ENTRY(dl)	slist;
196};
197
198SLIST_HEAD(, dl) dlist = SLIST_HEAD_INITIALIZER(&dlist);
199
200int
201list(const int fd)
202{
203	char *disklist, *p, *p2, *p3;
204	int unit;
205	size_t dll;
206	struct dl *dp, *di, *dn;
207
208	if (sysctlbyname("kern.disks", NULL, &dll, NULL, 0) == -1)
209		err(1, "sysctlbyname: kern.disks");
210	if ( (disklist = malloc(dll)) == NULL)
211		err(1, "malloc");
212	if (sysctlbyname("kern.disks", disklist, &dll, NULL, NULL) == -1)
213		err(1, "sysctlbyname: kern.disks");
214
215	for (p = disklist;
216	     (p2 = strsep(&p, " ")) != NULL;) {
217		if (strncmp(p2, MD_NAME, sizeof(MD_NAME) - 1) != 0)
218			continue;
219		p2 += sizeof(MD_NAME) - 1;
220		unit = strtoul(p2, &p3, 10);
221		if (p2 == p3)
222			continue;
223		dp = calloc(sizeof *dp, 1);
224		dp->unit = unit;
225		dn = SLIST_FIRST(&dlist);
226		if (dn == NULL || dn->unit > unit) {
227			SLIST_INSERT_HEAD(&dlist, dp, slist);
228		} else {
229			SLIST_FOREACH(di, &dlist, slist) {
230				dn = SLIST_NEXT(di, slist);
231				if (dn == NULL || dn->unit > unit) {
232					SLIST_INSERT_AFTER(di, dp, slist);
233					break;
234				}
235			}
236		}
237	}
238	SLIST_FOREACH(di, &dlist, slist)
239		query(fd, di->unit);
240	while (!SLIST_EMPTY(&dlist)) {
241		di = SLIST_FIRST(&dlist);
242		SLIST_REMOVE_HEAD(&dlist, slist);
243		free(di);
244	}
245	free(disklist);
246	return (0);
247}
248
249int
250query(const int fd, const int unit)
251{
252
253	mdio.md_unit = unit;
254
255	if (ioctl(fd, MDIOCQUERY, &mdio) < 0)
256		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
257
258	switch (mdio.md_type) {
259	case MD_MALLOC:
260		(void)printf("%s%d\tmalloc\t%d KBytes\n", MD_NAME,
261		    mdio.md_unit, mdio.md_size / 2);
262		break;
263	case MD_PRELOAD:
264		(void)printf("%s%d\tpreload\t%d KBytes\n", MD_NAME,
265		    mdio.md_unit, mdio.md_size / 2);
266		break;
267	case MD_SWAP:
268		(void)printf("%s%d\tswap\t%d KBytes\n", MD_NAME,
269		    mdio.md_unit, mdio.md_size / 2);
270		break;
271	case MD_VNODE:
272		(void)printf("%s%d\tvnode\t%d KBytes\n", MD_NAME,
273		    mdio.md_unit, mdio.md_size / 2);
274		break;
275	}
276
277	return (0);
278}
279
280void
281mdmaybeload(void)
282{
283        struct module_stat mstat;
284        int fileid, modid;
285        const char *name = "md";
286	char *cp;
287
288        /* scan files in kernel */
289        mstat.version = sizeof(struct module_stat);
290        for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
291                /* scan modules in file */
292                for (modid = kldfirstmod(fileid); modid > 0;
293                     modid = modfnext(modid)) {
294                        if (modstat(modid, &mstat) < 0)
295                                continue;
296                        /* strip bus name if present */
297                        if ((cp = strchr(mstat.name, '/')) != NULL) {
298                                cp++;
299                        } else {
300                                cp = mstat.name;
301                        }
302                        /* already loaded? */
303                        if (!strcmp(name, cp))
304                                return;
305                }
306        }
307        /* not present, we should try to load it */
308        kldload(name);
309}
310
311