Move the IOCTLTRIM ioctl to a better header and rename it to DAIOCTRIM.
[dragonfly.git] / sbin / swapon / swapon.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved.
30 * @(#)swapon.c 8.1 (Berkeley) 6/5/93
31 * $FreeBSD: src/sbin/swapon/swapon.c,v 1.8.2.2 2001/07/30 10:30:11 dd Exp $
32 */
33
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <sys/sysctl.h>
37#include <sys/linker.h>
38#include <sys/diskslice.h>
39#include <vm/vm_param.h>
40
41#include <err.h>
42#include <errno.h>
43#include <fstab.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <fcntl.h>
49#include <libutil.h>
50
51#include <bus/cam/scsi/scsi_daio.h>
52
53static void usage(void);
54static int swap_on_off(char *name, int doingall, int trim, int ask);
55static char *docrypt(char *fs_spec, int pass);
56static void swaplist(int lflag, int sflag, int hflag);
57
58static int qflag;
59
60enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
61
62int
63main(int argc, char **argv)
64{
65 struct fstab *fsp;
66 char *ptr;
67 int ret;
68 int ch;
69 int doall, sflag, lflag, hflag, eflag, cflag, iflag;
70
71 if ((ptr = strrchr(argv[0], '/')) == NULL)
72 ptr = argv[0];
73 if (strstr(ptr, "swapon"))
74 which_prog = SWAPON;
75 else if (strstr(ptr, "swapoff"))
76 which_prog = SWAPOFF;
77 orig_prog = which_prog;
78
79 sflag = lflag = hflag = doall = eflag = cflag = iflag = 0;
80 while ((ch = getopt(argc, argv, "AacdeghiklmqsU")) != -1) {
81 switch((char)ch) {
82 case 'A':
83 if (which_prog == SWAPCTL) {
84 doall = 1;
85 which_prog = SWAPON;
86 } else {
87 usage();
88 }
89 break;
90 case 'a':
91 if (which_prog == SWAPON || which_prog == SWAPOFF)
92 doall = 1;
93 else
94 which_prog = SWAPON;
95 break;
96 case 'd':
97 if (which_prog == SWAPCTL)
98 which_prog = SWAPOFF;
99 else
100 usage();
101 break;
102 case 'c':
103 cflag = 1;
104 break;
105 case 'e':
106 eflag = 1;
107 break;
108 case 'g':
109 hflag = 'G';
110 break;
111 case 'h':
112 hflag = 'H';
113 break;
114 case 'i':
115 iflag = 1;
116 break;
117 case 'k':
118 hflag = 'K';
119 break;
120 case 'l':
121 lflag = 1;
122 break;
123 case 'm':
124 hflag = 'M';
125 break;
126 case 'q':
127 if (which_prog == SWAPON || which_prog == SWAPOFF)
128 qflag = 1;
129 break;
130 case 's':
131 sflag = 1;
132 break;
133 case 'U':
134 if (which_prog == SWAPCTL) {
135 doall = 1;
136 which_prog = SWAPOFF;
137 } else {
138 usage();
139 }
140 break;
141 case '?':
142 default:
143 usage();
144 }
145 }
146 argv += optind;
147
148 ret = 0;
149 if (which_prog == SWAPON || which_prog == SWAPOFF) {
150 if (doall) {
151 while ((fsp = getfsent()) != NULL) {
152 char *fs_spec;
153 int dotrim = eflag;
154
155 if (strcmp(fsp->fs_type, FSTAB_SW))
156 continue;
157 if (strstr(fsp->fs_mntops, "noauto"))
158 continue;
159
160 if (strstr(fsp->fs_mntops, "notrim"))
161 dotrim = 0;
162 else if (strstr(fsp->fs_mntops, "trim"))
163 dotrim = 1;
164
165 if (cflag || strstr(fsp->fs_mntops, "crypt"))
166 fs_spec = docrypt(fsp->fs_spec, 1);
167 else
168 fs_spec = strdup(fsp->fs_spec);
169 if (swap_on_off(fs_spec, 1, dotrim, iflag)) {
170 ret = 1;
171 } else {
172 if (cflag ||
173 strstr(fsp->fs_mntops, "crypt")) {
174 docrypt(fsp->fs_spec, 2);
175 }
176 if (!qflag) {
177 printf("%s: %sing %s as swap "
178 "device\n",
179 getprogname(),
180 (which_prog == SWAPOFF ?
181 "remov" : "add"),
182 fs_spec);
183 }
184 }
185 free(fs_spec);
186 }
187 } else if (*argv == NULL) {
188 usage();
189 }
190 for (; *argv; ++argv) {
191 char *ospec = getdevpath(*argv, 0);
192 char *fs_spec;
193 if (cflag)
194 fs_spec = docrypt(ospec, 1);
195 else
196 fs_spec = strdup(ospec);
197 if (swap_on_off(fs_spec, 0, eflag, iflag)) {
198 ret = 1;
199 } else {
200 if (cflag)
201 docrypt(ospec, 2);
202 if (!qflag) {
203 printf("%s: %sing %s as swap device\n",
204 getprogname(),
205 (which_prog == SWAPOFF ?
206 "remov" : "add"),
207 fs_spec);
208 }
209 }
210 free(fs_spec);
211 }
212 } else {
213 if (lflag || sflag)
214 swaplist(lflag, sflag, hflag);
215 else
216 usage();
217 }
218 exit(ret);
219}
220
221static
222char *
223docrypt(char *fs_spec, int pass)
224{
225 char *id;
226 char *res;
227 char *buf;
228
229 if ((id = strrchr(fs_spec, '/')) == NULL)
230 id = fs_spec;
231 else
232 ++id;
233 asprintf(&id, "swap-%s", id);
234 asprintf(&res, "/dev/mapper/%s", id);
235
236 switch(which_prog) {
237 case SWAPOFF:
238 if (pass != 2)
239 break;
240 asprintf(&buf, "/sbin/cryptsetup remove %s", id);
241 system(buf);
242 free(buf);
243 free(id);
244 break;
245 case SWAPON:
246 if (pass != 1)
247 break;
248 if (kldfind("dm_target_crypt") < 0)
249 kldload("dm_target_crypt");
250
251 asprintf(&buf,
252 "/sbin/cryptsetup --key-file /dev/urandom "
253 "--key-size 256 create %s %s",
254 id, fs_spec);
255 if (qflag == 0)
256 printf("%s\n", buf);
257 system(buf);
258 free(buf);
259 free(id);
260
261 /*
262 * NOTE: Don't revert to /dev/da* on error because this could
263 * inadvertently add both /dev/da* and
264 * /dev/mapper/swap-da*.
265 *
266 * Allow the swapon operation to report failure or
267 * report a duplicate.
268 */
269 break;
270 default:
271 free(res);
272 free(id);
273 res = strdup(fs_spec);
274 break;
275 }
276
277 if (pass == 2) {
278 free (res);
279 res = NULL;
280 }
281 return res;
282}
283
284/*
285 * TRIM the device
286 */
287static
288void
289trim_volume(char * name)
290{
291 struct partinfo pinfo;
292 int fd,i,n;
293 size_t bytes = 0,ksize;
294 char *xswbuf;
295 struct xswdev *xsw;
296
297
298 /*
299 * Determine if this device is already being used by swap without
300 * calling swapon().
301 */
302 if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
303 bytes == 0) {
304 err(1, "sysctlbyname()");
305 }
306
307 xswbuf = malloc(bytes);
308 if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
309 bytes == 0) {
310 free(xswbuf);
311 err(1, "sysctlbyname()");
312 }
313
314 ksize = ((struct xswdev *)xswbuf)->xsw_size;
315 n = (int)(bytes / ksize);
316 for (i = 0; i < n; ++i) {
317 xsw = (void *)((char *)xswbuf + i * ksize);
318
319 if (xsw->xsw_dev == NODEV )
320 continue;
321 if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
322 name + strlen("/dev/"))) {
323 warnx("%s: device already a swap device", name);
324 exit(1);
325 }
326 }
327
328 /*
329 * Get the size and offset of this parititon/device
330 */
331 fd = open(name, O_RDWR);
332 if (fd < 0)
333 err(1, "Unable to open %s R+W", name);
334 if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
335 printf("Cannot trim regular file\n");
336 usage ();
337 }
338 off_t ioarg[2];
339
340 /*Trim the Device*/
341 ioarg[0] = pinfo.media_offset;
342 ioarg[1] = pinfo.media_size;
343 printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
344 (unsigned long long)ioarg[0]/512,
345 (unsigned long long)ioarg[1]/512);
346 if (ioctl(fd, DAIOCTRIM, ioarg) < 0) {
347 printf("Device trim failed\n");
348 usage ();
349 }
350 close(fd);
351}
352
353static int
354swap_on_off(char *name, int doingall, int trim, int ask)
355{
356
357 if (ask && which_prog == SWAPON) {
358 printf("Do you really want to use device %s as a swap device ?\n", name);
359 printf("You might loose data. [Y/N]");
360
361 int c = fgetc(stdin);
362 printf("\n");
363 if (c != 'y' && c != 'Y')
364 return(1);
365
366 }
367 if (which_prog == SWAPON && trim) {
368 char sysctl_name[64];
369 int trim_enabled = 0;
370 size_t olen = sizeof(trim_enabled);
371 char *dev_name = strdup(name);
372 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
373 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
374 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
375 if (qflag == 0) {
376 printf("TRIM not supported on %s, "
377 "ignoring\n",
378 name);
379 }
380 errno = 0;
381 } else if (!trim_enabled) {
382 if (qflag == 0) {
383 printf("TRIM not enabled on %s (%s), "
384 "ignoring\n",
385 name, sysctl_name);
386 }
387 } else {
388 trim_volume(name);
389 }
390 }
391 if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
392 switch(errno) {
393 case EBUSY:
394 if (!doingall)
395 warnx("%s: device already in use", name);
396 break;
397 case EINVAL:
398 if (which_prog == SWAPON)
399 warnx("%s: NSWAPDEV limit reached", name);
400 else if (!doingall)
401 warn("%s", name);
402 break;
403 default:
404 warn("%s", name);
405 break;
406 }
407 return(1);
408 }
409 return(0);
410}
411
412static void
413usage(void)
414{
415 fprintf(stderr, "usage: %s ", getprogname());
416 switch (orig_prog) {
417 case SWAPON:
418 case SWAPOFF:
419 fprintf(stderr, "-aeiq | file ...\n");
420 break;
421 case SWAPCTL:
422 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
423 break;
424 }
425 exit(1);
426}
427
428static void
429sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
430 long blocksize)
431{
432 if (hflag == 'H') {
433 char tmp[16];
434
435 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
436 HN_B | HN_NOSPACE | HN_DECIMAL);
437 snprintf(buf, bufsize, "%*s", hlen, tmp);
438 } else {
439 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
440 }
441}
442
443static void
444swaplist(int lflag, int sflag, int hflag)
445{
446 size_t ksize, bytes = 0;
447 char *xswbuf;
448 struct xswdev *xsw;
449 int hlen, pagesize;
450 int i, n;
451 long blocksize;
452 long long total, used, tmp_total, tmp_used;
453 char buf[32];
454
455 pagesize = getpagesize();
456 switch(hflag) {
457 case 'G':
458 blocksize = 1024 * 1024 * 1024;
459 strlcpy(buf, "1GB-blocks", sizeof(buf));
460 hlen = 10;
461 break;
462 case 'H':
463 blocksize = -1;
464 strlcpy(buf, "Bytes", sizeof(buf));
465 hlen = 10;
466 break;
467 case 'K':
468 blocksize = 1024;
469 strlcpy(buf, "1kB-blocks", sizeof(buf));
470 hlen = 10;
471 break;
472 case 'M':
473 blocksize = 1024 * 1024;
474 strlcpy(buf, "1MB-blocks", sizeof(buf));
475 hlen = 10;
476 break;
477 default:
478 getbsize(&hlen, &blocksize);
479 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
480 break;
481 }
482
483 if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
484 err(1, "sysctlbyname()");
485 if (bytes == 0)
486 err(1, "sysctlbyname()");
487
488 xswbuf = malloc(bytes);
489 if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
490 free(xswbuf);
491 err(1, "sysctlbyname()");
492 }
493 if (bytes == 0) {
494 free(xswbuf);
495 err(1, "sysctlbyname()");
496 }
497
498 /*
499 * Calculate size of xsw entry returned by kernel (it can be larger
500 * than the one we have if there is a version mismatch).
501 */
502 ksize = ((struct xswdev *)xswbuf)->xsw_size;
503 n = (int)(bytes / ksize);
504
505 if (lflag) {
506 printf("%-13s %*s %*s\n",
507 "Device:",
508 hlen, buf,
509 hlen, "Used:");
510 }
511
512 total = used = tmp_total = tmp_used = 0;
513 for (i = 0; i < n; ++i) {
514 xsw = (void *)((char *)xswbuf + i * ksize);
515
516 if (xsw->xsw_nblks == 0)
517 continue;
518
519 tmp_total = (long long)xsw->xsw_nblks * pagesize;
520 tmp_used = (long long)xsw->xsw_used * pagesize;
521 total += tmp_total;
522 used += tmp_used;
523 if (lflag) {
524 sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
525 blocksize);
526 if (xsw->xsw_dev == NODEV) {
527 printf("%-13s %s ", "[NFS swap]", buf);
528 } else {
529 printf("/dev/%-8s %s ",
530 devname(xsw->xsw_dev, S_IFCHR), buf);
531 }
532
533 sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
534 blocksize);
535 printf("%s\n", buf);
536 }
537 }
538
539 if (sflag) {
540 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
541 printf("Total: %s ", buf);
542 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
543 printf("%s\n", buf);
544 }
545}