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