xref: /illumos-gate/usr/src/cmd/rcm_daemon/common/swap_rcm.c (revision 648495d6a097a39eaebc7e56d25f2463ff3bba65)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * RCM module providing support for swap areas
30  * during reconfiguration operations.
31  */
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"
48 
49 /* cache flags */
50 #define	SWAP_CACHE_NEW		0x01
51 #define	SWAP_CACHE_STALE	0x02
52 #define	SWAP_CACHE_OFFLINED	0x04
53 
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"
57 
58 /* LP64 hard code */
59 #define	MAXOFFSET_STRLEN	20
60 
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;
68 
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;
77 
78 static swap_file_t	*cache;
79 static mutex_t		cache_lock;
80 
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 **);
95 
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 *);
113 
114 static struct rcm_mod_ops swap_ops =
115 {
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
128 };
129 
130 struct rcm_mod_ops *
131 rcm_mod_init()
132 {
133 	return (&swap_ops);
134 }
135 
136 const char *
137 rcm_mod_info()
138 {
139 	return ("RCM Swap module 1.5");
140 }
141 
142 int
143 rcm_mod_fini()
144 {
145 	free_cache();
146 	(void) mutex_destroy(&cache_lock);
147 
148 	return (RCM_SUCCESS);
149 }
150 
151 static int
152 swap_register(rcm_handle_t *hdl)
153 {
154 	return (update_cache(hdl));
155 }
156 
157 static int
158 swap_unregister(rcm_handle_t *hdl)
159 {
160 	swap_file_t	*sf;
161 
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);
167 	}
168 	(void) mutex_unlock(&cache_lock);
169 
170 	return (RCM_SUCCESS);
171 }
172 
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)
177 {
178 	assert(rsrcname != NULL && infostr != NULL);
179 
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);
186 	}
187 	(void) mutex_unlock(&cache_lock);
188 	(void) alloc_usage(infostr);
189 
190 	return (RCM_SUCCESS);
191 }
192 
193 /*
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.
197  */
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)
202 {
203 	swap_file_t	*sf;
204 	int		rv;
205 
206 	assert(rsrcname != NULL && errstr != NULL);
207 
208 	if (flags & RCM_QUERY)
209 		return (RCM_SUCCESS);
210 
211 	(void) mutex_lock(&cache_lock);
212 	if ((sf = cache_lookup(rsrcname)) == NULL) {
213 		(void) mutex_unlock(&cache_lock);
214 		return (RCM_SUCCESS);
215 	}
216 
217 	rv = swap_delete(sf, errstr);
218 	(void) mutex_unlock(&cache_lock);
219 
220 	return (rv);
221 }
222 
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)
227 {
228 	swap_file_t	*sf;
229 	int		rv;
230 
231 	assert(rsrcname != NULL && errstr != NULL);
232 
233 	(void) mutex_lock(&cache_lock);
234 	if ((sf = cache_lookup(rsrcname)) == NULL) {
235 		(void) mutex_unlock(&cache_lock);
236 		return (RCM_SUCCESS);
237 	}
238 
239 	rv = swap_add(sf, errstr);
240 	(void) mutex_unlock(&cache_lock);
241 
242 	return (rv);
243 }
244 
245 /*
246  * By default, reject offline request. If forced, attempt to
247  * delete swap. Fail if operation will unconfigure dump device.
248  */
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)
253 {
254 	swap_file_t	*sf;
255 	int		rv;
256 
257 	assert(rsrcname != NULL && errstr != NULL);
258 
259 	if ((flags & RCM_FORCE) && (flags & RCM_QUERY))
260 		return (RCM_SUCCESS);
261 
262 	(void) mutex_lock(&cache_lock);
263 	if ((sf = cache_lookup(rsrcname)) == NULL) {
264 		(void) mutex_unlock(&cache_lock);
265 		return (RCM_SUCCESS);
266 	}
267 
268 	if (flags & RCM_FORCE) {
269 		rv = swap_delete(sf, errstr);
270 		(void) mutex_unlock(&cache_lock);
271 		return (rv);
272 	}
273 	/* default reject */
274 	(void) mutex_unlock(&cache_lock);
275 	(void) alloc_usage(errstr);
276 
277 	return (RCM_FAILURE);
278 }
279 
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)
284 {
285 	swap_file_t	*sf;
286 	int		rv;
287 
288 	assert(rsrcname != NULL && errstr != NULL);
289 
290 	(void) mutex_lock(&cache_lock);
291 	if ((sf = cache_lookup(rsrcname)) == NULL) {
292 		(void) mutex_unlock(&cache_lock);
293 		return (RCM_SUCCESS);
294 	}
295 
296 	rv = swap_add(sf, errstr);
297 	(void) mutex_unlock(&cache_lock);
298 
299 	return (rv);
300 }
301 
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)
306 {
307 	swap_file_t	*sf;
308 
309 	assert(rsrcname != NULL);
310 
311 	(void) mutex_lock(&cache_lock);
312 	if ((sf = cache_lookup(rsrcname)) == NULL) {
313 		(void) mutex_unlock(&cache_lock);
314 		return (RCM_SUCCESS);
315 	}
316 	/* RCM framework handles unregistration */
317 	cache_remove(sf);
318 	swap_file_free(sf);
319 	(void) mutex_unlock(&cache_lock);
320 
321 	return (RCM_SUCCESS);
322 }
323 
324 /*
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.
330  *
331  * Call with cache_lock held.
332  */
333 static int
334 swap_delete(swap_file_t *sf, char **errstr)
335 {
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;
343 
344 	if (get_dumpdev(dumpdev) == 0 && dumpdev[0] == '\0')
345 		have_dump = 0;
346 
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;
351 		}
352 
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;
361 		}
362 		sa->cache_flags |= SWAP_CACHE_OFFLINED;
363 
364 		/*
365 		 * Fail on removal of dump device.
366 		 */
367 		if (have_dump == 0)
368 			continue;
369 
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");
376 
377 			/*
378 			 * Restore dump
379 			 */
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");
390 			}
391 			*errstr = strdup(gettext("unable to relocate dump"));
392 			rv = RCM_FAILURE;
393 			goto out;
394 		}
395 	}
396 	sf->cache_flags |= SWAP_CACHE_OFFLINED;
397 out:
398 	return (rv);
399 }
400 
401 /*
402  * Invoke swap(1M) to add each registered swap area.
403  *
404  * Call with cache_lock held.
405  */
406 static int
407 swap_add(swap_file_t *sf, char **errstr)
408 {
409 	swap_area_t	*sa;
410 	char		cmd[sizeof (SWAP_ADD) + MAXPATHLEN +
411 			    (2 * MAXOFFSET_STRLEN)];
412 	int		stat;
413 	int		rv = RCM_SUCCESS;
414 
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;
419 		}
420 
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;
432 		}
433 	}
434 
435 	return (rv);
436 }
437 
438 static int
439 update_cache(rcm_handle_t *hdl)
440 {
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;
446 
447 	if ((swt = sys_swaptbl()) == NULL) {
448 		rcm_log_message(RCM_ERROR, "failed to read "
449 		    "current swap configuration\n");
450 		return (RCM_FAILURE);
451 	}
452 
453 	(void) mutex_lock(&cache_lock);
454 
455 	/*
456 	 * cache pass 1 - mark everyone stale
457 	 */
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;
462 		}
463 	}
464 
465 	/*
466 	 * add new entries
467 	 */
468 	for (i = 0; i < swt->swt_n; i++) {
469 		if (swt->swt_ent[i].ste_flags & (ST_INDEL|ST_DOINGDEL)) {
470 			continue;
471 		}
472 
473 		/*
474 		 * assure swap_file_t
475 		 */
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);
481 			}
482 			sf->cache_flags |= SWAP_CACHE_NEW;
483 			cache_insert(sf);
484 		} else {
485 			sf->cache_flags &= ~SWAP_CACHE_STALE;
486 		}
487 
488 		/*
489 		 * assure swap_area_t
490 		 */
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);
495 			}
496 			swap_area_add(sf, sa);
497 		} else {
498 			sa->cache_flags &= ~SWAP_CACHE_STALE;
499 		}
500 	}
501 
502 	free(swt);
503 
504 	/*
505 	 * cache pass 2
506 	 *
507 	 * swap_file_t - skip offlined, register new, unregister/remove stale
508 	 * swap_area_t - skip offlined, remove stale
509 	 */
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;
518 			}
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;
525 			}
526 			sa = sa->next;
527 		}
528 
529 		if (sf->cache_flags & SWAP_CACHE_OFFLINED) {
530 			sf->cache_flags &= ~SWAP_CACHE_STALE;
531 			sf = sf->next;
532 			continue;
533 		}
534 
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);
540 			}
541 			stale_sf = sf;
542 			sf = sf->next;
543 			cache_remove(stale_sf);
544 			swap_file_free(stale_sf);
545 			continue;
546 		}
547 
548 		if (!(sf->cache_flags & SWAP_CACHE_NEW)) {
549 			sf = sf->next;
550 			continue;
551 		}
552 
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;
562 		}
563 		sf = sf->next;
564 	}
565 	(void) mutex_unlock(&cache_lock);
566 
567 	return (rv);
568 }
569 
570 /*
571  * Returns system swap table.
572  */
573 static swaptbl_t *
574 sys_swaptbl()
575 {
576 	swaptbl_t	*swt;
577 	char		*cp;
578 	int		i, n;
579 	size_t		tbl_size;
580 
581 	if ((n = swapctl(SC_GETNSWP, NULL)) == -1)
582 		return (NULL);
583 
584 	tbl_size = sizeof (int) + n * sizeof (swapent_t) + n * MAXPATHLEN;
585 	if ((swt = (swaptbl_t *)malloc(tbl_size)) == NULL)
586 		return (NULL);
587 
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;
593 	}
594 
595 	if ((n = swapctl(SC_LIST, swt)) == -1) {
596 		free(swt);
597 		return (NULL);
598 	}
599 
600 	if (n != swt->swt_n) {
601 		/* mismatch, try again */
602 		free(swt);
603 		return (sys_swaptbl());
604 	}
605 
606 	return (swt);
607 }
608 
609 static int
610 get_dumpdev(char dumpdev[])
611 {
612 	int	fd;
613 	int	rv = 0;
614 	char	*err;
615 
616 	if ((fd = open("/dev/dump", O_RDONLY)) == -1) {
617 		rcm_log_message(RCM_ERROR, "failed to open /dev/dump\n");
618 		return (-1);
619 	}
620 
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;
628 		}
629 	}
630 	(void) close(fd);
631 
632 	return (rv);
633 }
634 
635 static void
636 free_cache(void)
637 {
638 	swap_file_t	*sf;
639 
640 	(void) mutex_lock(&cache_lock);
641 	while ((sf = cache) != NULL) {
642 		cache = cache->next;
643 		swap_file_free(sf);
644 	}
645 	(void) mutex_unlock(&cache_lock);
646 
647 }
648 
649 /*
650  * Call with cache_lock held.
651  */
652 static void
653 swap_file_free(swap_file_t *sf)
654 {
655 	swap_area_t	*sa;
656 
657 	assert(sf != NULL);
658 
659 	while ((sa = sf->areas) != NULL) {
660 		sf->areas = sf->areas->next;
661 		free(sa);
662 	}
663 	free(sf);
664 }
665 
666 /*
667  * Call with cache_lock held.
668  */
669 static void
670 cache_insert(swap_file_t *ent)
671 {
672 	ent->next = cache;
673 	if (ent->next)
674 		ent->next->prev = ent;
675 	ent->prev = NULL;
676 	cache = ent;
677 }
678 
679 /*
680  * Call with cache_lock held.
681  */
682 static swap_file_t *
683 cache_lookup(char *rsrc)
684 {
685 	swap_file_t	*sf;
686 
687 	for (sf = cache; sf != NULL; sf = sf->next) {
688 		if (strcmp(rsrc, sf->path) == 0) {
689 			return (sf);
690 		}
691 	}
692 	return (NULL);
693 }
694 
695 /*
696  * Call with cache_lock held.
697  */
698 static void
699 cache_remove(swap_file_t *ent)
700 {
701 	assert(ent != NULL);
702 
703 	if (ent->next != NULL) {
704 		ent->next->prev = ent->prev;
705 	}
706 	if (ent->prev != NULL) {
707 		ent->prev->next = ent->next;
708 	} else {
709 		cache = ent->next;
710 	}
711 	ent->next = NULL;
712 	ent->prev = NULL;
713 }
714 
715 /*
716  * Call with cache_lock held.
717  */
718 static void
719 swap_area_add(swap_file_t *sf, swap_area_t *sa)
720 {
721 	sa->next = sf->areas;
722 	if (sa->next)
723 		sa->next->prev = sa;
724 	sa->prev = NULL;
725 	sf->areas = sa;
726 }
727 
728 /*
729  * Call with cache_lock held.
730  */
731 static void
732 swap_area_remove(swap_file_t *sf, swap_area_t *ent)
733 {
734 	assert(sf != NULL && ent != NULL);
735 
736 	if (ent->next != NULL) {
737 		ent->next->prev = ent->prev;
738 	}
739 	if (ent->prev != NULL) {
740 		ent->prev->next = ent->next;
741 	} else {
742 		sf->areas = ent->next;
743 	}
744 	ent->next = NULL;
745 	ent->prev = NULL;
746 }
747 
748 static swap_file_t *
749 swap_file_alloc(char *rsrc)
750 {
751 	swap_file_t	*sf;
752 
753 	if ((sf = calloc(1, sizeof (*sf))) == NULL) {
754 		rcm_log_message(RCM_ERROR, "calloc failure\n");
755 		return (NULL);
756 	}
757 	(void) strlcpy(sf->path, rsrc, sizeof (sf->path));
758 
759 	return (sf);
760 }
761 
762 static swap_area_t *
763 swap_area_alloc(swapent_t *swt_ent)
764 {
765 	swap_area_t	*sa;
766 
767 	if ((sa = calloc(1, sizeof (*sa))) == NULL) {
768 		rcm_log_message(RCM_ERROR, "calloc failure\n");
769 		return (NULL);
770 	}
771 	sa->start = swt_ent->ste_start;
772 	sa->len = swt_ent->ste_length;
773 
774 	return (sa);
775 }
776 
777 /*
778  * Call with cache_lock held.
779  */
780 static swap_area_t *
781 swap_area_lookup(swap_file_t *sf, swapent_t *swt_ent)
782 {
783 	swap_area_t	*sa;
784 
785 	assert(sf != NULL && swt_ent != NULL);
786 	assert(strcmp(sf->path, swt_ent->ste_path) == 0);
787 
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);
792 		}
793 	}
794 	return (NULL);
795 }
796 
797 /*
798  * All-purpose usage string.
799  */
800 static int
801 alloc_usage(char **cpp)
802 {
803 	if ((*cpp = strdup(gettext("swap area"))) == NULL) {
804 		rcm_log_message(RCM_ERROR, "strdup failure\n");
805 		return (-1);
806 	}
807 	return (0);
808 }
809 
810 static void
811 log_cmd_status(int stat)
812 {
813 	char	*err;
814 
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);
823 	}
824 }
825