xref: /illumos-gate/usr/src/cmd/rcm_daemon/common/swap_rcm.c (revision 0245b61fd282e95735b173b8d95be0d6688163b4)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * RCM module providing support for swap areas
28  * during reconfiguration operations.
29  */
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <thread.h>
34 #include <synch.h>
35 #include <strings.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <libintl.h>
39 #include <sys/types.h>
40 #include <sys/swap.h>
41 #include <sys/stat.h>
42 #include <sys/param.h>
43 #include <sys/dumpadm.h>
44 #include <sys/wait.h>
45 #include "rcm_module.h"
46 
47 /* cache flags */
48 #define	SWAP_CACHE_NEW		0x01
49 #define	SWAP_CACHE_STALE	0x02
50 #define	SWAP_CACHE_OFFLINED	0x04
51 
52 #define	SWAP_CMD		"/usr/sbin/swap"
53 #define	SWAP_DELETE		SWAP_CMD" -d %s %ld"
54 #define	SWAP_ADD		SWAP_CMD" -a %s %ld %ld"
55 
56 /* LP64 hard code */
57 #define	MAXOFFSET_STRLEN	20
58 
59 typedef struct swap_file {
60 	char			path[MAXPATHLEN];
61 	int			cache_flags;
62 	struct swap_area	*areas;
63 	struct swap_file	*next;
64 	struct swap_file	*prev;
65 } swap_file_t;
66 
67 /* swap file may have multiple swap areas */
68 typedef struct swap_area {
69 	off_t			start;
70 	off_t			len;
71 	int			cache_flags;
72 	struct swap_area	*next;
73 	struct swap_area	*prev;
74 } swap_area_t;
75 
76 static swap_file_t	*cache;
77 static mutex_t		cache_lock;
78 
79 static int		swap_register(rcm_handle_t *);
80 static int		swap_unregister(rcm_handle_t *);
81 static int		swap_getinfo(rcm_handle_t *, char *, id_t, uint_t,
82 			    char **, char **, nvlist_t *, rcm_info_t **);
83 static int		swap_suspend(rcm_handle_t *, char *, id_t, timespec_t *,
84 			    uint_t, char **, rcm_info_t **);
85 static int		swap_resume(rcm_handle_t *, char *, id_t, uint_t,
86 			    char **, rcm_info_t **);
87 static int		swap_offline(rcm_handle_t *, char *, id_t, uint_t,
88 			    char **, rcm_info_t **);
89 static int		swap_online(rcm_handle_t *, char *, id_t, uint_t,
90 			    char **, rcm_info_t **);
91 static int		swap_remove(rcm_handle_t *, char *, id_t, uint_t,
92 			    char **, rcm_info_t **);
93 
94 static int		alloc_usage(char **);
95 static void		cache_insert(swap_file_t *);
96 static swap_file_t	*cache_lookup(char *);
97 static void		cache_remove(swap_file_t *);
98 static void		free_cache(void);
99 static int		get_dumpdev(char []);
100 static void		log_cmd_status(int);
101 static int		swap_add(swap_file_t *, char **);
102 static void		swap_area_add(swap_file_t *, swap_area_t *);
103 static swap_area_t	*swap_area_alloc(swapent_t *);
104 static swap_area_t	*swap_area_lookup(swap_file_t *, swapent_t *);
105 static void		swap_area_remove(swap_file_t *, swap_area_t *);
106 static int		swap_delete(swap_file_t *, char **);
107 static swap_file_t	*swap_file_alloc(char *);
108 static void		swap_file_free(swap_file_t *);
109 static swaptbl_t	*sys_swaptbl(void);
110 static int		update_cache(rcm_handle_t *);
111 
112 static struct rcm_mod_ops swap_ops =
113 {
114 	RCM_MOD_OPS_VERSION,
115 	swap_register,
116 	swap_unregister,
117 	swap_getinfo,
118 	swap_suspend,
119 	swap_resume,
120 	swap_offline,
121 	swap_online,
122 	swap_remove,
123 	NULL,
124 	NULL,
125 	NULL
126 };
127 
128 struct rcm_mod_ops *
129 rcm_mod_init()
130 {
131 	return (&swap_ops);
132 }
133 
134 const char *
135 rcm_mod_info()
136 {
137 	return ("RCM Swap module 1.5");
138 }
139 
140 int
141 rcm_mod_fini()
142 {
143 	free_cache();
144 	(void) mutex_destroy(&cache_lock);
145 
146 	return (RCM_SUCCESS);
147 }
148 
149 static int
150 swap_register(rcm_handle_t *hdl)
151 {
152 	return (update_cache(hdl));
153 }
154 
155 static int
156 swap_unregister(rcm_handle_t *hdl)
157 {
158 	swap_file_t	*sf;
159 
160 	(void) mutex_lock(&cache_lock);
161 	while ((sf = cache) != NULL) {
162 		cache = cache->next;
163 		(void) rcm_unregister_interest(hdl, sf->path, 0);
164 		swap_file_free(sf);
165 	}
166 	(void) mutex_unlock(&cache_lock);
167 
168 	return (RCM_SUCCESS);
169 }
170 
171 /*ARGSUSED*/
172 static int
173 swap_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
174     char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent)
175 {
176 	assert(rsrcname != NULL && infostr != NULL);
177 
178 	(void) mutex_lock(&cache_lock);
179 	if (cache_lookup(rsrcname) == NULL) {
180 		rcm_log_message(RCM_ERROR, "unknown resource: %s\n",
181 		    rsrcname);
182 		(void) mutex_unlock(&cache_lock);
183 		return (RCM_FAILURE);
184 	}
185 	(void) mutex_unlock(&cache_lock);
186 	(void) alloc_usage(infostr);
187 
188 	return (RCM_SUCCESS);
189 }
190 
191 /*
192  * Remove swap space to maintain availability of anonymous pages
193  * during device suspension. Swap will be reconfigured upon resume.
194  * Fail if operation will unconfigure dump device.
195  */
196 /*ARGSUSED*/
197 static int
198 swap_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval,
199     uint_t flags, char **errstr, rcm_info_t **dependent)
200 {
201 	swap_file_t	*sf;
202 	int		rv;
203 
204 	assert(rsrcname != NULL && errstr != NULL);
205 
206 	if (flags & RCM_QUERY)
207 		return (RCM_SUCCESS);
208 
209 	(void) mutex_lock(&cache_lock);
210 	if ((sf = cache_lookup(rsrcname)) == NULL) {
211 		(void) mutex_unlock(&cache_lock);
212 		return (RCM_SUCCESS);
213 	}
214 
215 	rv = swap_delete(sf, errstr);
216 	(void) mutex_unlock(&cache_lock);
217 
218 	return (rv);
219 }
220 
221 /*ARGSUSED*/
222 static int
223 swap_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
224     char **errstr, rcm_info_t **dependent)
225 {
226 	swap_file_t	*sf;
227 	int		rv;
228 
229 	assert(rsrcname != NULL && errstr != NULL);
230 
231 	(void) mutex_lock(&cache_lock);
232 	if ((sf = cache_lookup(rsrcname)) == NULL) {
233 		(void) mutex_unlock(&cache_lock);
234 		return (RCM_SUCCESS);
235 	}
236 
237 	rv = swap_add(sf, errstr);
238 	(void) mutex_unlock(&cache_lock);
239 
240 	return (rv);
241 }
242 
243 /*
244  * By default, reject offline request. If forced, attempt to
245  * delete swap. Fail if operation will unconfigure dump device.
246  */
247 /*ARGSUSED*/
248 static int
249 swap_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
250     char **errstr, rcm_info_t **dependent)
251 {
252 	swap_file_t	*sf;
253 	int		rv;
254 
255 	assert(rsrcname != NULL && errstr != NULL);
256 
257 	if ((flags & RCM_FORCE) && (flags & RCM_QUERY))
258 		return (RCM_SUCCESS);
259 
260 	(void) mutex_lock(&cache_lock);
261 	if ((sf = cache_lookup(rsrcname)) == NULL) {
262 		(void) mutex_unlock(&cache_lock);
263 		return (RCM_SUCCESS);
264 	}
265 
266 	if (flags & RCM_FORCE) {
267 		rv = swap_delete(sf, errstr);
268 		(void) mutex_unlock(&cache_lock);
269 		return (rv);
270 	}
271 	/* default reject */
272 	(void) mutex_unlock(&cache_lock);
273 	(void) alloc_usage(errstr);
274 
275 	return (RCM_FAILURE);
276 }
277 
278 /*ARGSUSED*/
279 static int
280 swap_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
281     char **errstr, rcm_info_t **dependent)
282 {
283 	swap_file_t	*sf;
284 	int		rv;
285 
286 	assert(rsrcname != NULL && errstr != NULL);
287 
288 	(void) mutex_lock(&cache_lock);
289 	if ((sf = cache_lookup(rsrcname)) == NULL) {
290 		(void) mutex_unlock(&cache_lock);
291 		return (RCM_SUCCESS);
292 	}
293 
294 	rv = swap_add(sf, errstr);
295 	(void) mutex_unlock(&cache_lock);
296 
297 	return (rv);
298 }
299 
300 /*ARGSUSED*/
301 static int
302 swap_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
303     char **errstr, rcm_info_t **dependent)
304 {
305 	swap_file_t	*sf;
306 
307 	assert(rsrcname != NULL);
308 
309 	(void) mutex_lock(&cache_lock);
310 	if ((sf = cache_lookup(rsrcname)) == NULL) {
311 		(void) mutex_unlock(&cache_lock);
312 		return (RCM_SUCCESS);
313 	}
314 	/* RCM framework handles unregistration */
315 	cache_remove(sf);
316 	swap_file_free(sf);
317 	(void) mutex_unlock(&cache_lock);
318 
319 	return (RCM_SUCCESS);
320 }
321 
322 /*
323  * Delete all swap areas for swap file.
324  * Invoke swap(8) instead of swapctl(2) to
325  * handle relocation of dump device.
326  * If dump device is configured, fail if
327  * unable to relocate dump.
328  *
329  * Call with cache_lock held.
330  */
331 static int
332 swap_delete(swap_file_t *sf, char **errstr)
333 {
334 	swap_area_t	*sa;
335 	char		cmd[sizeof (SWAP_DELETE) + MAXPATHLEN +
336 			    MAXOFFSET_STRLEN];
337 	char		dumpdev[MAXPATHLEN];
338 	int		have_dump = 1;
339 	int		stat;
340 	int		rv = RCM_SUCCESS;
341 
342 	if (get_dumpdev(dumpdev) == 0 && dumpdev[0] == '\0')
343 		have_dump = 0;
344 
345 	for (sa = sf->areas; sa != NULL; sa = sa->next) {
346 		/* swap(8) is not idempotent */
347 		if (sa->cache_flags & SWAP_CACHE_OFFLINED) {
348 			continue;
349 		}
350 
351 		(void) snprintf(cmd, sizeof (cmd), SWAP_DELETE, sf->path,
352 		    sa->start);
353 		rcm_log_message(RCM_TRACE1, "%s\n", cmd);
354 		if ((stat = rcm_exec_cmd(cmd)) != 0) {
355 			log_cmd_status(stat);
356 			*errstr = strdup(gettext("unable to delete swap"));
357 			rv = RCM_FAILURE;
358 			goto out;
359 		}
360 		sa->cache_flags |= SWAP_CACHE_OFFLINED;
361 
362 		/*
363 		 * Fail on removal of dump device.
364 		 */
365 		if (have_dump == 0)
366 			continue;
367 
368 		if (get_dumpdev(dumpdev) != 0) {
369 			rcm_log_message(RCM_WARNING, "unable to "
370 			    "check for removal of dump device\n");
371 		} else if (dumpdev[0] == '\0') {
372 			rcm_log_message(RCM_DEBUG, "removed dump: "
373 			    "attempting recovery\n");
374 
375 			/*
376 			 * Restore dump
377 			 */
378 			(void) snprintf(cmd, sizeof (cmd), SWAP_ADD,
379 				sf->path, sa->start, sa->len);
380 			rcm_log_message(RCM_TRACE1, "%s\n", cmd);
381 			if ((stat = rcm_exec_cmd(cmd)) != 0) {
382 				log_cmd_status(stat);
383 				rcm_log_message(RCM_ERROR,
384 				    "failed to restore dump\n");
385 			} else {
386 				sa->cache_flags &= ~SWAP_CACHE_OFFLINED;
387 				rcm_log_message(RCM_DEBUG, "dump restored\n");
388 			}
389 			*errstr = strdup(gettext("unable to relocate dump"));
390 			rv = RCM_FAILURE;
391 			goto out;
392 		}
393 	}
394 	sf->cache_flags |= SWAP_CACHE_OFFLINED;
395 out:
396 	return (rv);
397 }
398 
399 /*
400  * Invoke swap(8) to add each registered swap area.
401  *
402  * Call with cache_lock held.
403  */
404 static int
405 swap_add(swap_file_t *sf, char **errstr)
406 {
407 	swap_area_t	*sa;
408 	char		cmd[sizeof (SWAP_ADD) + MAXPATHLEN +
409 			    (2 * MAXOFFSET_STRLEN)];
410 	int		stat;
411 	int		rv = RCM_SUCCESS;
412 
413 	for (sa = sf->areas; sa != NULL; sa = sa->next) {
414 		/* swap(8) is not idempotent */
415 		if (!(sa->cache_flags & SWAP_CACHE_OFFLINED)) {
416 			continue;
417 		}
418 
419 		(void) snprintf(cmd, sizeof (cmd),
420 			SWAP_ADD, sf->path, sa->start, sa->len);
421 		rcm_log_message(RCM_TRACE1, "%s\n", cmd);
422 		if ((stat = rcm_exec_cmd(cmd)) != 0) {
423 			log_cmd_status(stat);
424 			*errstr = strdup(gettext("unable to add swap"));
425 			rv = RCM_FAILURE;
426 			break;
427 		} else {
428 			sa->cache_flags &= ~SWAP_CACHE_OFFLINED;
429 			sf->cache_flags &= ~SWAP_CACHE_OFFLINED;
430 		}
431 	}
432 
433 	return (rv);
434 }
435 
436 static int
437 update_cache(rcm_handle_t *hdl)
438 {
439 	swaptbl_t	*swt;
440 	swap_file_t	*sf, *stale_sf;
441 	swap_area_t	*sa, *stale_sa;
442 	int		i;
443 	int		rv = RCM_SUCCESS;
444 
445 	if ((swt = sys_swaptbl()) == NULL) {
446 		rcm_log_message(RCM_ERROR, "failed to read "
447 		    "current swap configuration\n");
448 		return (RCM_FAILURE);
449 	}
450 
451 	(void) mutex_lock(&cache_lock);
452 
453 	/*
454 	 * cache pass 1 - mark everyone stale
455 	 */
456 	for (sf = cache; sf != NULL; sf = sf->next) {
457 		sf->cache_flags |= SWAP_CACHE_STALE;
458 		for (sa = sf->areas; sa != NULL; sa = sa->next) {
459 			sa->cache_flags |= SWAP_CACHE_STALE;
460 		}
461 	}
462 
463 	/*
464 	 * add new entries
465 	 */
466 	for (i = 0; i < swt->swt_n; i++) {
467 		if (swt->swt_ent[i].ste_flags & (ST_INDEL|ST_DOINGDEL)) {
468 			continue;
469 		}
470 
471 		/*
472 		 * assure swap_file_t
473 		 */
474 		if ((sf = cache_lookup(swt->swt_ent[i].ste_path)) == NULL) {
475 			if ((sf = swap_file_alloc(swt->swt_ent[i].ste_path)) ==
476 			    NULL) {
477 				free(swt);
478 				return (RCM_FAILURE);
479 			}
480 			sf->cache_flags |= SWAP_CACHE_NEW;
481 			cache_insert(sf);
482 		} else {
483 			sf->cache_flags &= ~SWAP_CACHE_STALE;
484 		}
485 
486 		/*
487 		 * assure swap_area_t
488 		 */
489 		if ((sa = swap_area_lookup(sf, &swt->swt_ent[i])) == NULL) {
490 			if ((sa = swap_area_alloc(&swt->swt_ent[i])) == NULL) {
491 				free(swt);
492 				return (RCM_FAILURE);
493 			}
494 			swap_area_add(sf, sa);
495 		} else {
496 			sa->cache_flags &= ~SWAP_CACHE_STALE;
497 		}
498 	}
499 
500 	free(swt);
501 
502 	/*
503 	 * cache pass 2
504 	 *
505 	 * swap_file_t - skip offlined, register new, unregister/remove stale
506 	 * swap_area_t - skip offlined, remove stale
507 	 */
508 	sf = cache;
509 	while (sf != NULL) {
510 		sa = sf->areas;
511 		while (sa != NULL) {
512 			if (sa->cache_flags & SWAP_CACHE_OFFLINED) {
513 				sa->cache_flags &= ~SWAP_CACHE_STALE;
514 				sa = sa->next;
515 				continue;
516 			}
517 			if (sa->cache_flags & SWAP_CACHE_STALE) {
518 				stale_sa = sa;
519 				sa = sa->next;
520 				swap_area_remove(sf, stale_sa);
521 				free(stale_sa);
522 				continue;
523 			}
524 			sa = sa->next;
525 		}
526 
527 		if (sf->cache_flags & SWAP_CACHE_OFFLINED) {
528 			sf->cache_flags &= ~SWAP_CACHE_STALE;
529 			sf = sf->next;
530 			continue;
531 		}
532 
533 		if (sf->cache_flags & SWAP_CACHE_STALE) {
534 			if (rcm_unregister_interest(hdl, sf->path, 0) !=
535 			    RCM_SUCCESS) {
536 				rcm_log_message(RCM_ERROR, "failed to register "
537 				    "%s\n", sf->path);
538 			}
539 			stale_sf = sf;
540 			sf = sf->next;
541 			cache_remove(stale_sf);
542 			swap_file_free(stale_sf);
543 			continue;
544 		}
545 
546 		if (!(sf->cache_flags & SWAP_CACHE_NEW)) {
547 			sf = sf->next;
548 			continue;
549 		}
550 
551 		if (rcm_register_interest(hdl, sf->path, 0, NULL) !=
552 		    RCM_SUCCESS) {
553 			rcm_log_message(RCM_ERROR, "failed to register %s\n",
554 			    sf->path);
555 			rv = RCM_FAILURE;
556 		} else {
557 			rcm_log_message(RCM_DEBUG, "registered %s\n",
558 			    sf->path);
559 			sf->cache_flags &= ~SWAP_CACHE_NEW;
560 		}
561 		sf = sf->next;
562 	}
563 	(void) mutex_unlock(&cache_lock);
564 
565 	return (rv);
566 }
567 
568 /*
569  * Returns system swap table.
570  */
571 static swaptbl_t *
572 sys_swaptbl()
573 {
574 	swaptbl_t	*swt;
575 	char		*cp;
576 	int		i, n;
577 	size_t		tbl_size;
578 
579 	if ((n = swapctl(SC_GETNSWP, NULL)) == -1)
580 		return (NULL);
581 
582 	tbl_size = sizeof (int) + n * sizeof (swapent_t) + n * MAXPATHLEN;
583 	if ((swt = (swaptbl_t *)malloc(tbl_size)) == NULL)
584 		return (NULL);
585 
586 	swt->swt_n = n;
587 	cp = (char *)swt + (sizeof (int) + n * sizeof (swapent_t));
588 	for (i = 0; i < n; i++) {
589 		swt->swt_ent[i].ste_path = cp;
590 		cp += MAXPATHLEN;
591 	}
592 
593 	if ((n = swapctl(SC_LIST, swt)) == -1) {
594 		free(swt);
595 		return (NULL);
596 	}
597 
598 	if (n != swt->swt_n) {
599 		/* mismatch, try again */
600 		free(swt);
601 		return (sys_swaptbl());
602 	}
603 
604 	return (swt);
605 }
606 
607 static int
608 get_dumpdev(char dumpdev[])
609 {
610 	int	fd;
611 	int	rv = 0;
612 	char	*err;
613 
614 	if ((fd = open("/dev/dump", O_RDONLY)) == -1) {
615 		rcm_log_message(RCM_ERROR, "failed to open /dev/dump\n");
616 		return (-1);
617 	}
618 
619 	if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) {
620 		if (errno == ENODEV) {
621 			dumpdev[0] = '\0';
622 		} else {
623 			rcm_log_message(RCM_ERROR, "ioctl: %s\n",
624 			    ((err = strerror(errno)) == NULL) ? "" : err);
625 			rv = -1;
626 		}
627 	}
628 	(void) close(fd);
629 
630 	return (rv);
631 }
632 
633 static void
634 free_cache(void)
635 {
636 	swap_file_t	*sf;
637 
638 	(void) mutex_lock(&cache_lock);
639 	while ((sf = cache) != NULL) {
640 		cache = cache->next;
641 		swap_file_free(sf);
642 	}
643 	(void) mutex_unlock(&cache_lock);
644 
645 }
646 
647 /*
648  * Call with cache_lock held.
649  */
650 static void
651 swap_file_free(swap_file_t *sf)
652 {
653 	swap_area_t	*sa;
654 
655 	assert(sf != NULL);
656 
657 	while ((sa = sf->areas) != NULL) {
658 		sf->areas = sf->areas->next;
659 		free(sa);
660 	}
661 	free(sf);
662 }
663 
664 /*
665  * Call with cache_lock held.
666  */
667 static void
668 cache_insert(swap_file_t *ent)
669 {
670 	ent->next = cache;
671 	if (ent->next)
672 		ent->next->prev = ent;
673 	ent->prev = NULL;
674 	cache = ent;
675 }
676 
677 /*
678  * Call with cache_lock held.
679  */
680 static swap_file_t *
681 cache_lookup(char *rsrc)
682 {
683 	swap_file_t	*sf;
684 
685 	for (sf = cache; sf != NULL; sf = sf->next) {
686 		if (strcmp(rsrc, sf->path) == 0) {
687 			return (sf);
688 		}
689 	}
690 	return (NULL);
691 }
692 
693 /*
694  * Call with cache_lock held.
695  */
696 static void
697 cache_remove(swap_file_t *ent)
698 {
699 	assert(ent != NULL);
700 
701 	if (ent->next != NULL) {
702 		ent->next->prev = ent->prev;
703 	}
704 	if (ent->prev != NULL) {
705 		ent->prev->next = ent->next;
706 	} else {
707 		cache = ent->next;
708 	}
709 	ent->next = NULL;
710 	ent->prev = NULL;
711 }
712 
713 /*
714  * Call with cache_lock held.
715  */
716 static void
717 swap_area_add(swap_file_t *sf, swap_area_t *sa)
718 {
719 	sa->next = sf->areas;
720 	if (sa->next)
721 		sa->next->prev = sa;
722 	sa->prev = NULL;
723 	sf->areas = sa;
724 }
725 
726 /*
727  * Call with cache_lock held.
728  */
729 static void
730 swap_area_remove(swap_file_t *sf, swap_area_t *ent)
731 {
732 	assert(sf != NULL && ent != NULL);
733 
734 	if (ent->next != NULL) {
735 		ent->next->prev = ent->prev;
736 	}
737 	if (ent->prev != NULL) {
738 		ent->prev->next = ent->next;
739 	} else {
740 		sf->areas = ent->next;
741 	}
742 	ent->next = NULL;
743 	ent->prev = NULL;
744 }
745 
746 static swap_file_t *
747 swap_file_alloc(char *rsrc)
748 {
749 	swap_file_t	*sf;
750 
751 	if ((sf = calloc(1, sizeof (*sf))) == NULL) {
752 		rcm_log_message(RCM_ERROR, "calloc failure\n");
753 		return (NULL);
754 	}
755 	(void) strlcpy(sf->path, rsrc, sizeof (sf->path));
756 
757 	return (sf);
758 }
759 
760 static swap_area_t *
761 swap_area_alloc(swapent_t *swt_ent)
762 {
763 	swap_area_t	*sa;
764 
765 	if ((sa = calloc(1, sizeof (*sa))) == NULL) {
766 		rcm_log_message(RCM_ERROR, "calloc failure\n");
767 		return (NULL);
768 	}
769 	sa->start = swt_ent->ste_start;
770 	sa->len = swt_ent->ste_length;
771 
772 	return (sa);
773 }
774 
775 /*
776  * Call with cache_lock held.
777  */
778 static swap_area_t *
779 swap_area_lookup(swap_file_t *sf, swapent_t *swt_ent)
780 {
781 	swap_area_t	*sa;
782 
783 	assert(sf != NULL && swt_ent != NULL);
784 	assert(strcmp(sf->path, swt_ent->ste_path) == 0);
785 
786 	for (sa = sf->areas; sa != NULL; sa = sa->next) {
787 		if (sa->start == swt_ent->ste_start &&
788 		    sa->len == swt_ent->ste_length) {
789 			return (sa);
790 		}
791 	}
792 	return (NULL);
793 }
794 
795 /*
796  * All-purpose usage string.
797  */
798 static int
799 alloc_usage(char **cpp)
800 {
801 	if ((*cpp = strdup(gettext("swap area"))) == NULL) {
802 		rcm_log_message(RCM_ERROR, "strdup failure\n");
803 		return (-1);
804 	}
805 	return (0);
806 }
807 
808 static void
809 log_cmd_status(int stat)
810 {
811 	char	*err;
812 
813 	if (stat == -1) {
814 		rcm_log_message(RCM_ERROR, "wait: %s\n",
815 		    ((err = strerror(errno)) == NULL) ? "" : err);
816 	} else if (WIFEXITED(stat)) {
817 		rcm_log_message(RCM_ERROR, "exit status: %d\n",
818 		    WEXITSTATUS(stat));
819 	} else {
820 		rcm_log_message(RCM_ERROR, "wait status: %d\n", stat);
821 	}
822 }
823