dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / rcm_daemon / common / swap_rcm.c
blob8b7bc0d2a9603db6181e8a7eac45fda8b2134568
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * RCM module providing support for swap areas
30 * during reconfiguration operations.
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <thread.h>
36 #include <synch.h>
37 #include <strings.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <libintl.h>
41 #include <sys/types.h>
42 #include <sys/swap.h>
43 #include <sys/stat.h>
44 #include <sys/param.h>
45 #include <sys/dumpadm.h>
46 #include <sys/wait.h>
47 #include "rcm_module.h"
49 /* cache flags */
50 #define SWAP_CACHE_NEW 0x01
51 #define SWAP_CACHE_STALE 0x02
52 #define SWAP_CACHE_OFFLINED 0x04
54 #define SWAP_CMD "/usr/sbin/swap"
55 #define SWAP_DELETE SWAP_CMD" -d %s %ld"
56 #define SWAP_ADD SWAP_CMD" -a %s %ld %ld"
58 /* LP64 hard code */
59 #define MAXOFFSET_STRLEN 20
61 typedef struct swap_file {
62 char path[MAXPATHLEN];
63 int cache_flags;
64 struct swap_area *areas;
65 struct swap_file *next;
66 struct swap_file *prev;
67 } swap_file_t;
69 /* swap file may have multiple swap areas */
70 typedef struct swap_area {
71 off_t start;
72 off_t len;
73 int cache_flags;
74 struct swap_area *next;
75 struct swap_area *prev;
76 } swap_area_t;
78 static swap_file_t *cache;
79 static mutex_t cache_lock;
81 static int swap_register(rcm_handle_t *);
82 static int swap_unregister(rcm_handle_t *);
83 static int swap_getinfo(rcm_handle_t *, char *, id_t, uint_t,
84 char **, char **, nvlist_t *, rcm_info_t **);
85 static int swap_suspend(rcm_handle_t *, char *, id_t, timespec_t *,
86 uint_t, char **, rcm_info_t **);
87 static int swap_resume(rcm_handle_t *, char *, id_t, uint_t,
88 char **, rcm_info_t **);
89 static int swap_offline(rcm_handle_t *, char *, id_t, uint_t,
90 char **, rcm_info_t **);
91 static int swap_online(rcm_handle_t *, char *, id_t, uint_t,
92 char **, rcm_info_t **);
93 static int swap_remove(rcm_handle_t *, char *, id_t, uint_t,
94 char **, rcm_info_t **);
96 static int alloc_usage(char **);
97 static void cache_insert(swap_file_t *);
98 static swap_file_t *cache_lookup(char *);
99 static void cache_remove(swap_file_t *);
100 static void free_cache(void);
101 static int get_dumpdev(char []);
102 static void log_cmd_status(int);
103 static int swap_add(swap_file_t *, char **);
104 static void swap_area_add(swap_file_t *, swap_area_t *);
105 static swap_area_t *swap_area_alloc(swapent_t *);
106 static swap_area_t *swap_area_lookup(swap_file_t *, swapent_t *);
107 static void swap_area_remove(swap_file_t *, swap_area_t *);
108 static int swap_delete(swap_file_t *, char **);
109 static swap_file_t *swap_file_alloc(char *);
110 static void swap_file_free(swap_file_t *);
111 static swaptbl_t *sys_swaptbl(void);
112 static int update_cache(rcm_handle_t *);
114 static struct rcm_mod_ops swap_ops =
116 RCM_MOD_OPS_VERSION,
117 swap_register,
118 swap_unregister,
119 swap_getinfo,
120 swap_suspend,
121 swap_resume,
122 swap_offline,
123 swap_online,
124 swap_remove,
125 NULL,
126 NULL,
127 NULL
130 struct rcm_mod_ops *
131 rcm_mod_init()
133 return (&swap_ops);
136 const char *
137 rcm_mod_info()
139 return ("RCM Swap module 1.5");
143 rcm_mod_fini()
145 free_cache();
146 (void) mutex_destroy(&cache_lock);
148 return (RCM_SUCCESS);
151 static int
152 swap_register(rcm_handle_t *hdl)
154 return (update_cache(hdl));
157 static int
158 swap_unregister(rcm_handle_t *hdl)
160 swap_file_t *sf;
162 (void) mutex_lock(&cache_lock);
163 while ((sf = cache) != NULL) {
164 cache = cache->next;
165 (void) rcm_unregister_interest(hdl, sf->path, 0);
166 swap_file_free(sf);
168 (void) mutex_unlock(&cache_lock);
170 return (RCM_SUCCESS);
173 /*ARGSUSED*/
174 static int
175 swap_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
176 char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent)
178 assert(rsrcname != NULL && infostr != NULL);
180 (void) mutex_lock(&cache_lock);
181 if (cache_lookup(rsrcname) == NULL) {
182 rcm_log_message(RCM_ERROR, "unknown resource: %s\n",
183 rsrcname);
184 (void) mutex_unlock(&cache_lock);
185 return (RCM_FAILURE);
187 (void) mutex_unlock(&cache_lock);
188 (void) alloc_usage(infostr);
190 return (RCM_SUCCESS);
194 * Remove swap space to maintain availability of anonymous pages
195 * during device suspension. Swap will be reconfigured upon resume.
196 * Fail if operation will unconfigure dump device.
198 /*ARGSUSED*/
199 static int
200 swap_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval,
201 uint_t flags, char **errstr, rcm_info_t **dependent)
203 swap_file_t *sf;
204 int rv;
206 assert(rsrcname != NULL && errstr != NULL);
208 if (flags & RCM_QUERY)
209 return (RCM_SUCCESS);
211 (void) mutex_lock(&cache_lock);
212 if ((sf = cache_lookup(rsrcname)) == NULL) {
213 (void) mutex_unlock(&cache_lock);
214 return (RCM_SUCCESS);
217 rv = swap_delete(sf, errstr);
218 (void) mutex_unlock(&cache_lock);
220 return (rv);
223 /*ARGSUSED*/
224 static int
225 swap_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
226 char **errstr, rcm_info_t **dependent)
228 swap_file_t *sf;
229 int rv;
231 assert(rsrcname != NULL && errstr != NULL);
233 (void) mutex_lock(&cache_lock);
234 if ((sf = cache_lookup(rsrcname)) == NULL) {
235 (void) mutex_unlock(&cache_lock);
236 return (RCM_SUCCESS);
239 rv = swap_add(sf, errstr);
240 (void) mutex_unlock(&cache_lock);
242 return (rv);
246 * By default, reject offline request. If forced, attempt to
247 * delete swap. Fail if operation will unconfigure dump device.
249 /*ARGSUSED*/
250 static int
251 swap_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
252 char **errstr, rcm_info_t **dependent)
254 swap_file_t *sf;
255 int rv;
257 assert(rsrcname != NULL && errstr != NULL);
259 if ((flags & RCM_FORCE) && (flags & RCM_QUERY))
260 return (RCM_SUCCESS);
262 (void) mutex_lock(&cache_lock);
263 if ((sf = cache_lookup(rsrcname)) == NULL) {
264 (void) mutex_unlock(&cache_lock);
265 return (RCM_SUCCESS);
268 if (flags & RCM_FORCE) {
269 rv = swap_delete(sf, errstr);
270 (void) mutex_unlock(&cache_lock);
271 return (rv);
273 /* default reject */
274 (void) mutex_unlock(&cache_lock);
275 (void) alloc_usage(errstr);
277 return (RCM_FAILURE);
280 /*ARGSUSED*/
281 static int
282 swap_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
283 char **errstr, rcm_info_t **dependent)
285 swap_file_t *sf;
286 int rv;
288 assert(rsrcname != NULL && errstr != NULL);
290 (void) mutex_lock(&cache_lock);
291 if ((sf = cache_lookup(rsrcname)) == NULL) {
292 (void) mutex_unlock(&cache_lock);
293 return (RCM_SUCCESS);
296 rv = swap_add(sf, errstr);
297 (void) mutex_unlock(&cache_lock);
299 return (rv);
302 /*ARGSUSED*/
303 static int
304 swap_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
305 char **errstr, rcm_info_t **dependent)
307 swap_file_t *sf;
309 assert(rsrcname != NULL);
311 (void) mutex_lock(&cache_lock);
312 if ((sf = cache_lookup(rsrcname)) == NULL) {
313 (void) mutex_unlock(&cache_lock);
314 return (RCM_SUCCESS);
316 /* RCM framework handles unregistration */
317 cache_remove(sf);
318 swap_file_free(sf);
319 (void) mutex_unlock(&cache_lock);
321 return (RCM_SUCCESS);
325 * Delete all swap areas for swap file.
326 * Invoke swap(1M) instead of swapctl(2) to
327 * handle relocation of dump device.
328 * If dump device is configured, fail if
329 * unable to relocate dump.
331 * Call with cache_lock held.
333 static int
334 swap_delete(swap_file_t *sf, char **errstr)
336 swap_area_t *sa;
337 char cmd[sizeof (SWAP_DELETE) + MAXPATHLEN +
338 MAXOFFSET_STRLEN];
339 char dumpdev[MAXPATHLEN];
340 int have_dump = 1;
341 int stat;
342 int rv = RCM_SUCCESS;
344 if (get_dumpdev(dumpdev) == 0 && dumpdev[0] == '\0')
345 have_dump = 0;
347 for (sa = sf->areas; sa != NULL; sa = sa->next) {
348 /* swap(1M) is not idempotent */
349 if (sa->cache_flags & SWAP_CACHE_OFFLINED) {
350 continue;
353 (void) snprintf(cmd, sizeof (cmd), SWAP_DELETE, sf->path,
354 sa->start);
355 rcm_log_message(RCM_TRACE1, "%s\n", cmd);
356 if ((stat = rcm_exec_cmd(cmd)) != 0) {
357 log_cmd_status(stat);
358 *errstr = strdup(gettext("unable to delete swap"));
359 rv = RCM_FAILURE;
360 goto out;
362 sa->cache_flags |= SWAP_CACHE_OFFLINED;
365 * Fail on removal of dump device.
367 if (have_dump == 0)
368 continue;
370 if (get_dumpdev(dumpdev) != 0) {
371 rcm_log_message(RCM_WARNING, "unable to "
372 "check for removal of dump device\n");
373 } else if (dumpdev[0] == '\0') {
374 rcm_log_message(RCM_DEBUG, "removed dump: "
375 "attempting recovery\n");
378 * Restore dump
380 (void) snprintf(cmd, sizeof (cmd), SWAP_ADD,
381 sf->path, sa->start, sa->len);
382 rcm_log_message(RCM_TRACE1, "%s\n", cmd);
383 if ((stat = rcm_exec_cmd(cmd)) != 0) {
384 log_cmd_status(stat);
385 rcm_log_message(RCM_ERROR,
386 "failed to restore dump\n");
387 } else {
388 sa->cache_flags &= ~SWAP_CACHE_OFFLINED;
389 rcm_log_message(RCM_DEBUG, "dump restored\n");
391 *errstr = strdup(gettext("unable to relocate dump"));
392 rv = RCM_FAILURE;
393 goto out;
396 sf->cache_flags |= SWAP_CACHE_OFFLINED;
397 out:
398 return (rv);
402 * Invoke swap(1M) to add each registered swap area.
404 * Call with cache_lock held.
406 static int
407 swap_add(swap_file_t *sf, char **errstr)
409 swap_area_t *sa;
410 char cmd[sizeof (SWAP_ADD) + MAXPATHLEN +
411 (2 * MAXOFFSET_STRLEN)];
412 int stat;
413 int rv = RCM_SUCCESS;
415 for (sa = sf->areas; sa != NULL; sa = sa->next) {
416 /* swap(1M) is not idempotent */
417 if (!(sa->cache_flags & SWAP_CACHE_OFFLINED)) {
418 continue;
421 (void) snprintf(cmd, sizeof (cmd),
422 SWAP_ADD, sf->path, sa->start, sa->len);
423 rcm_log_message(RCM_TRACE1, "%s\n", cmd);
424 if ((stat = rcm_exec_cmd(cmd)) != 0) {
425 log_cmd_status(stat);
426 *errstr = strdup(gettext("unable to add swap"));
427 rv = RCM_FAILURE;
428 break;
429 } else {
430 sa->cache_flags &= ~SWAP_CACHE_OFFLINED;
431 sf->cache_flags &= ~SWAP_CACHE_OFFLINED;
435 return (rv);
438 static int
439 update_cache(rcm_handle_t *hdl)
441 swaptbl_t *swt;
442 swap_file_t *sf, *stale_sf;
443 swap_area_t *sa, *stale_sa;
444 int i;
445 int rv = RCM_SUCCESS;
447 if ((swt = sys_swaptbl()) == NULL) {
448 rcm_log_message(RCM_ERROR, "failed to read "
449 "current swap configuration\n");
450 return (RCM_FAILURE);
453 (void) mutex_lock(&cache_lock);
456 * cache pass 1 - mark everyone stale
458 for (sf = cache; sf != NULL; sf = sf->next) {
459 sf->cache_flags |= SWAP_CACHE_STALE;
460 for (sa = sf->areas; sa != NULL; sa = sa->next) {
461 sa->cache_flags |= SWAP_CACHE_STALE;
466 * add new entries
468 for (i = 0; i < swt->swt_n; i++) {
469 if (swt->swt_ent[i].ste_flags & (ST_INDEL|ST_DOINGDEL)) {
470 continue;
474 * assure swap_file_t
476 if ((sf = cache_lookup(swt->swt_ent[i].ste_path)) == NULL) {
477 if ((sf = swap_file_alloc(swt->swt_ent[i].ste_path)) ==
478 NULL) {
479 free(swt);
480 return (RCM_FAILURE);
482 sf->cache_flags |= SWAP_CACHE_NEW;
483 cache_insert(sf);
484 } else {
485 sf->cache_flags &= ~SWAP_CACHE_STALE;
489 * assure swap_area_t
491 if ((sa = swap_area_lookup(sf, &swt->swt_ent[i])) == NULL) {
492 if ((sa = swap_area_alloc(&swt->swt_ent[i])) == NULL) {
493 free(swt);
494 return (RCM_FAILURE);
496 swap_area_add(sf, sa);
497 } else {
498 sa->cache_flags &= ~SWAP_CACHE_STALE;
502 free(swt);
505 * cache pass 2
507 * swap_file_t - skip offlined, register new, unregister/remove stale
508 * swap_area_t - skip offlined, remove stale
510 sf = cache;
511 while (sf != NULL) {
512 sa = sf->areas;
513 while (sa != NULL) {
514 if (sa->cache_flags & SWAP_CACHE_OFFLINED) {
515 sa->cache_flags &= ~SWAP_CACHE_STALE;
516 sa = sa->next;
517 continue;
519 if (sa->cache_flags & SWAP_CACHE_STALE) {
520 stale_sa = sa;
521 sa = sa->next;
522 swap_area_remove(sf, stale_sa);
523 free(stale_sa);
524 continue;
526 sa = sa->next;
529 if (sf->cache_flags & SWAP_CACHE_OFFLINED) {
530 sf->cache_flags &= ~SWAP_CACHE_STALE;
531 sf = sf->next;
532 continue;
535 if (sf->cache_flags & SWAP_CACHE_STALE) {
536 if (rcm_unregister_interest(hdl, sf->path, 0) !=
537 RCM_SUCCESS) {
538 rcm_log_message(RCM_ERROR, "failed to register "
539 "%s\n", sf->path);
541 stale_sf = sf;
542 sf = sf->next;
543 cache_remove(stale_sf);
544 swap_file_free(stale_sf);
545 continue;
548 if (!(sf->cache_flags & SWAP_CACHE_NEW)) {
549 sf = sf->next;
550 continue;
553 if (rcm_register_interest(hdl, sf->path, 0, NULL) !=
554 RCM_SUCCESS) {
555 rcm_log_message(RCM_ERROR, "failed to register %s\n",
556 sf->path);
557 rv = RCM_FAILURE;
558 } else {
559 rcm_log_message(RCM_DEBUG, "registered %s\n",
560 sf->path);
561 sf->cache_flags &= ~SWAP_CACHE_NEW;
563 sf = sf->next;
565 (void) mutex_unlock(&cache_lock);
567 return (rv);
571 * Returns system swap table.
573 static swaptbl_t *
574 sys_swaptbl()
576 swaptbl_t *swt;
577 char *cp;
578 int i, n;
579 size_t tbl_size;
581 if ((n = swapctl(SC_GETNSWP, NULL)) == -1)
582 return (NULL);
584 tbl_size = sizeof (int) + n * sizeof (swapent_t) + n * MAXPATHLEN;
585 if ((swt = (swaptbl_t *)malloc(tbl_size)) == NULL)
586 return (NULL);
588 swt->swt_n = n;
589 cp = (char *)swt + (sizeof (int) + n * sizeof (swapent_t));
590 for (i = 0; i < n; i++) {
591 swt->swt_ent[i].ste_path = cp;
592 cp += MAXPATHLEN;
595 if ((n = swapctl(SC_LIST, swt)) == -1) {
596 free(swt);
597 return (NULL);
600 if (n != swt->swt_n) {
601 /* mismatch, try again */
602 free(swt);
603 return (sys_swaptbl());
606 return (swt);
609 static int
610 get_dumpdev(char dumpdev[])
612 int fd;
613 int rv = 0;
614 char *err;
616 if ((fd = open("/dev/dump", O_RDONLY)) == -1) {
617 rcm_log_message(RCM_ERROR, "failed to open /dev/dump\n");
618 return (-1);
621 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) {
622 if (errno == ENODEV) {
623 dumpdev[0] = '\0';
624 } else {
625 rcm_log_message(RCM_ERROR, "ioctl: %s\n",
626 ((err = strerror(errno)) == NULL) ? "" : err);
627 rv = -1;
630 (void) close(fd);
632 return (rv);
635 static void
636 free_cache(void)
638 swap_file_t *sf;
640 (void) mutex_lock(&cache_lock);
641 while ((sf = cache) != NULL) {
642 cache = cache->next;
643 swap_file_free(sf);
645 (void) mutex_unlock(&cache_lock);
650 * Call with cache_lock held.
652 static void
653 swap_file_free(swap_file_t *sf)
655 swap_area_t *sa;
657 assert(sf != NULL);
659 while ((sa = sf->areas) != NULL) {
660 sf->areas = sf->areas->next;
661 free(sa);
663 free(sf);
667 * Call with cache_lock held.
669 static void
670 cache_insert(swap_file_t *ent)
672 ent->next = cache;
673 if (ent->next)
674 ent->next->prev = ent;
675 ent->prev = NULL;
676 cache = ent;
680 * Call with cache_lock held.
682 static swap_file_t *
683 cache_lookup(char *rsrc)
685 swap_file_t *sf;
687 for (sf = cache; sf != NULL; sf = sf->next) {
688 if (strcmp(rsrc, sf->path) == 0) {
689 return (sf);
692 return (NULL);
696 * Call with cache_lock held.
698 static void
699 cache_remove(swap_file_t *ent)
701 assert(ent != NULL);
703 if (ent->next != NULL) {
704 ent->next->prev = ent->prev;
706 if (ent->prev != NULL) {
707 ent->prev->next = ent->next;
708 } else {
709 cache = ent->next;
711 ent->next = NULL;
712 ent->prev = NULL;
716 * Call with cache_lock held.
718 static void
719 swap_area_add(swap_file_t *sf, swap_area_t *sa)
721 sa->next = sf->areas;
722 if (sa->next)
723 sa->next->prev = sa;
724 sa->prev = NULL;
725 sf->areas = sa;
729 * Call with cache_lock held.
731 static void
732 swap_area_remove(swap_file_t *sf, swap_area_t *ent)
734 assert(sf != NULL && ent != NULL);
736 if (ent->next != NULL) {
737 ent->next->prev = ent->prev;
739 if (ent->prev != NULL) {
740 ent->prev->next = ent->next;
741 } else {
742 sf->areas = ent->next;
744 ent->next = NULL;
745 ent->prev = NULL;
748 static swap_file_t *
749 swap_file_alloc(char *rsrc)
751 swap_file_t *sf;
753 if ((sf = calloc(1, sizeof (*sf))) == NULL) {
754 rcm_log_message(RCM_ERROR, "calloc failure\n");
755 return (NULL);
757 (void) strlcpy(sf->path, rsrc, sizeof (sf->path));
759 return (sf);
762 static swap_area_t *
763 swap_area_alloc(swapent_t *swt_ent)
765 swap_area_t *sa;
767 if ((sa = calloc(1, sizeof (*sa))) == NULL) {
768 rcm_log_message(RCM_ERROR, "calloc failure\n");
769 return (NULL);
771 sa->start = swt_ent->ste_start;
772 sa->len = swt_ent->ste_length;
774 return (sa);
778 * Call with cache_lock held.
780 static swap_area_t *
781 swap_area_lookup(swap_file_t *sf, swapent_t *swt_ent)
783 swap_area_t *sa;
785 assert(sf != NULL && swt_ent != NULL);
786 assert(strcmp(sf->path, swt_ent->ste_path) == 0);
788 for (sa = sf->areas; sa != NULL; sa = sa->next) {
789 if (sa->start == swt_ent->ste_start &&
790 sa->len == swt_ent->ste_length) {
791 return (sa);
794 return (NULL);
798 * All-purpose usage string.
800 static int
801 alloc_usage(char **cpp)
803 if ((*cpp = strdup(gettext("swap area"))) == NULL) {
804 rcm_log_message(RCM_ERROR, "strdup failure\n");
805 return (-1);
807 return (0);
810 static void
811 log_cmd_status(int stat)
813 char *err;
815 if (stat == -1) {
816 rcm_log_message(RCM_ERROR, "wait: %s\n",
817 ((err = strerror(errno)) == NULL) ? "" : err);
818 } else if (WIFEXITED(stat)) {
819 rcm_log_message(RCM_ERROR, "exit status: %d\n",
820 WEXITSTATUS(stat));
821 } else {
822 rcm_log_message(RCM_ERROR, "wait status: %d\n", stat);