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