xref: /freebsd/sbin/mdconfig/mdconfig.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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$
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 
27 int	 list(const int);
28 int	 query(const int, const int);
29 
30 struct md_ioctl mdio;
31 
32 enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
33 
34 void mdmaybeload(void);
35 void usage(void);
36 
37 void
38 usage()
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 
50 int
51 main(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 
193 struct dl {
194 	int		unit;
195 	SLIST_ENTRY(dl)	slist;
196 };
197 
198 SLIST_HEAD(, dl) dlist = SLIST_HEAD_INITIALIZER(&dlist);
199 
200 int
201 list(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 
249 int
250 query(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 
280 void
281 mdmaybeload(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