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]
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.
41 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/dumpadm.h>
47 #include "rcm_module.h"
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"
59 #define MAXOFFSET_STRLEN 20
61 typedef struct swap_file
{
62 char path
[MAXPATHLEN
];
64 struct swap_area
*areas
;
65 struct swap_file
*next
;
66 struct swap_file
*prev
;
69 /* swap file may have multiple swap areas */
70 typedef struct swap_area
{
74 struct swap_area
*next
;
75 struct swap_area
*prev
;
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
=
139 return ("RCM Swap module 1.5");
146 (void) mutex_destroy(&cache_lock
);
148 return (RCM_SUCCESS
);
152 swap_register(rcm_handle_t
*hdl
)
154 return (update_cache(hdl
));
158 swap_unregister(rcm_handle_t
*hdl
)
162 (void) mutex_lock(&cache_lock
);
163 while ((sf
= cache
) != NULL
) {
165 (void) rcm_unregister_interest(hdl
, sf
->path
, 0);
168 (void) mutex_unlock(&cache_lock
);
170 return (RCM_SUCCESS
);
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",
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.
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
)
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
);
225 swap_resume(rcm_handle_t
*hdl
, char *rsrcname
, id_t id
, uint_t flags
,
226 char **errstr
, rcm_info_t
**dependent
)
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
);
246 * By default, reject offline request. If forced, attempt to
247 * delete swap. Fail if operation will unconfigure dump device.
251 swap_offline(rcm_handle_t
*hdl
, char *rsrcname
, id_t id
, uint_t flags
,
252 char **errstr
, rcm_info_t
**dependent
)
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
);
274 (void) mutex_unlock(&cache_lock
);
275 (void) alloc_usage(errstr
);
277 return (RCM_FAILURE
);
282 swap_online(rcm_handle_t
*hdl
, char *rsrcname
, id_t id
, uint_t flags
,
283 char **errstr
, rcm_info_t
**dependent
)
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
);
304 swap_remove(rcm_handle_t
*hdl
, char *rsrcname
, id_t id
, uint_t flags
,
305 char **errstr
, rcm_info_t
**dependent
)
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 */
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.
334 swap_delete(swap_file_t
*sf
, char **errstr
)
337 char cmd
[sizeof (SWAP_DELETE
) + MAXPATHLEN
+
339 char dumpdev
[MAXPATHLEN
];
342 int rv
= RCM_SUCCESS
;
344 if (get_dumpdev(dumpdev
) == 0 && dumpdev
[0] == '\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
) {
353 (void) snprintf(cmd
, sizeof (cmd
), SWAP_DELETE
, sf
->path
,
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"));
362 sa
->cache_flags
|= SWAP_CACHE_OFFLINED
;
365 * Fail on removal of dump device.
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");
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");
388 sa
->cache_flags
&= ~SWAP_CACHE_OFFLINED
;
389 rcm_log_message(RCM_DEBUG
, "dump restored\n");
391 *errstr
= strdup(gettext("unable to relocate dump"));
396 sf
->cache_flags
|= SWAP_CACHE_OFFLINED
;
402 * Invoke swap(1M) to add each registered swap area.
404 * Call with cache_lock held.
407 swap_add(swap_file_t
*sf
, char **errstr
)
410 char cmd
[sizeof (SWAP_ADD
) + MAXPATHLEN
+
411 (2 * MAXOFFSET_STRLEN
)];
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
)) {
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"));
430 sa
->cache_flags
&= ~SWAP_CACHE_OFFLINED
;
431 sf
->cache_flags
&= ~SWAP_CACHE_OFFLINED
;
439 update_cache(rcm_handle_t
*hdl
)
442 swap_file_t
*sf
, *stale_sf
;
443 swap_area_t
*sa
, *stale_sa
;
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
;
468 for (i
= 0; i
< swt
->swt_n
; i
++) {
469 if (swt
->swt_ent
[i
].ste_flags
& (ST_INDEL
|ST_DOINGDEL
)) {
476 if ((sf
= cache_lookup(swt
->swt_ent
[i
].ste_path
)) == NULL
) {
477 if ((sf
= swap_file_alloc(swt
->swt_ent
[i
].ste_path
)) ==
480 return (RCM_FAILURE
);
482 sf
->cache_flags
|= SWAP_CACHE_NEW
;
485 sf
->cache_flags
&= ~SWAP_CACHE_STALE
;
491 if ((sa
= swap_area_lookup(sf
, &swt
->swt_ent
[i
])) == NULL
) {
492 if ((sa
= swap_area_alloc(&swt
->swt_ent
[i
])) == NULL
) {
494 return (RCM_FAILURE
);
496 swap_area_add(sf
, sa
);
498 sa
->cache_flags
&= ~SWAP_CACHE_STALE
;
507 * swap_file_t - skip offlined, register new, unregister/remove stale
508 * swap_area_t - skip offlined, remove stale
514 if (sa
->cache_flags
& SWAP_CACHE_OFFLINED
) {
515 sa
->cache_flags
&= ~SWAP_CACHE_STALE
;
519 if (sa
->cache_flags
& SWAP_CACHE_STALE
) {
522 swap_area_remove(sf
, stale_sa
);
529 if (sf
->cache_flags
& SWAP_CACHE_OFFLINED
) {
530 sf
->cache_flags
&= ~SWAP_CACHE_STALE
;
535 if (sf
->cache_flags
& SWAP_CACHE_STALE
) {
536 if (rcm_unregister_interest(hdl
, sf
->path
, 0) !=
538 rcm_log_message(RCM_ERROR
, "failed to register "
543 cache_remove(stale_sf
);
544 swap_file_free(stale_sf
);
548 if (!(sf
->cache_flags
& SWAP_CACHE_NEW
)) {
553 if (rcm_register_interest(hdl
, sf
->path
, 0, NULL
) !=
555 rcm_log_message(RCM_ERROR
, "failed to register %s\n",
559 rcm_log_message(RCM_DEBUG
, "registered %s\n",
561 sf
->cache_flags
&= ~SWAP_CACHE_NEW
;
565 (void) mutex_unlock(&cache_lock
);
571 * Returns system swap table.
581 if ((n
= swapctl(SC_GETNSWP
, NULL
)) == -1)
584 tbl_size
= sizeof (int) + n
* sizeof (swapent_t
) + n
* MAXPATHLEN
;
585 if ((swt
= (swaptbl_t
*)malloc(tbl_size
)) == NULL
)
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
;
595 if ((n
= swapctl(SC_LIST
, swt
)) == -1) {
600 if (n
!= swt
->swt_n
) {
601 /* mismatch, try again */
603 return (sys_swaptbl());
610 get_dumpdev(char dumpdev
[])
616 if ((fd
= open("/dev/dump", O_RDONLY
)) == -1) {
617 rcm_log_message(RCM_ERROR
, "failed to open /dev/dump\n");
621 if (ioctl(fd
, DIOCGETDEV
, dumpdev
) == -1) {
622 if (errno
== ENODEV
) {
625 rcm_log_message(RCM_ERROR
, "ioctl: %s\n",
626 ((err
= strerror(errno
)) == NULL
) ? "" : err
);
640 (void) mutex_lock(&cache_lock
);
641 while ((sf
= cache
) != NULL
) {
645 (void) mutex_unlock(&cache_lock
);
650 * Call with cache_lock held.
653 swap_file_free(swap_file_t
*sf
)
659 while ((sa
= sf
->areas
) != NULL
) {
660 sf
->areas
= sf
->areas
->next
;
667 * Call with cache_lock held.
670 cache_insert(swap_file_t
*ent
)
674 ent
->next
->prev
= ent
;
680 * Call with cache_lock held.
683 cache_lookup(char *rsrc
)
687 for (sf
= cache
; sf
!= NULL
; sf
= sf
->next
) {
688 if (strcmp(rsrc
, sf
->path
) == 0) {
696 * Call with cache_lock held.
699 cache_remove(swap_file_t
*ent
)
703 if (ent
->next
!= NULL
) {
704 ent
->next
->prev
= ent
->prev
;
706 if (ent
->prev
!= NULL
) {
707 ent
->prev
->next
= ent
->next
;
716 * Call with cache_lock held.
719 swap_area_add(swap_file_t
*sf
, swap_area_t
*sa
)
721 sa
->next
= sf
->areas
;
729 * Call with cache_lock held.
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
;
742 sf
->areas
= ent
->next
;
749 swap_file_alloc(char *rsrc
)
753 if ((sf
= calloc(1, sizeof (*sf
))) == NULL
) {
754 rcm_log_message(RCM_ERROR
, "calloc failure\n");
757 (void) strlcpy(sf
->path
, rsrc
, sizeof (sf
->path
));
763 swap_area_alloc(swapent_t
*swt_ent
)
767 if ((sa
= calloc(1, sizeof (*sa
))) == NULL
) {
768 rcm_log_message(RCM_ERROR
, "calloc failure\n");
771 sa
->start
= swt_ent
->ste_start
;
772 sa
->len
= swt_ent
->ste_length
;
778 * Call with cache_lock held.
781 swap_area_lookup(swap_file_t
*sf
, swapent_t
*swt_ent
)
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
) {
798 * All-purpose usage string.
801 alloc_usage(char **cpp
)
803 if ((*cpp
= strdup(gettext("swap area"))) == NULL
) {
804 rcm_log_message(RCM_ERROR
, "strdup failure\n");
811 log_cmd_status(int stat
)
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",
822 rcm_log_message(RCM_ERROR
, "wait status: %d\n", stat
);