xref: /illumos-gate/usr/src/cmd/rcm_daemon/common/dump_rcm.c (revision defc4c8acfa01dba1ef3c13ca0cafccfcede51c0)
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 for managing dump device during dynamic
30  * reconfiguration.
31  */
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <thread.h>
37 #include <synch.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <libintl.h>
41 #include <sys/dumpadm.h>
42 #include <sys/param.h>
43 #include <sys/wait.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include "rcm_module.h"
47 
48 /* cache flags */
49 #define	DUMP_CACHE_NEW		0x01
50 #define	DUMP_CACHE_STALE	0x02
51 #define	DUMP_CACHE_OFFLINED	0x04
52 
53 #define	DUMPADM			"/usr/sbin/dumpadm -d "
54 #define	DUMPADM_SWAP		DUMPADM"swap"
55 
56 typedef struct dump_conf {
57 	char		device[MAXPATHLEN];
58 	int		conf_flags;		/* defs in <sys/dumpadm.h> */
59 	int		cache_flags;
60 	struct dump_conf *next;
61 	struct dump_conf *prev;
62 } dump_conf_t;
63 
64 /*
65  * Registration cache.
66  *
67  * N.B.	Although we currently only support a single
68  *	dump device, the cache is multi-entry since there
69  *	may be multiple outstanding registrations.
70  */
71 static dump_conf_t	*cache;
72 static mutex_t		cache_lock;
73 
74 static int		dump_register(rcm_handle_t *);
75 static int		dump_unregister(rcm_handle_t *);
76 static int		dump_getinfo(rcm_handle_t *, char *, id_t, uint_t,
77 			    char **, char **, nvlist_t *, rcm_info_t **);
78 static int		dump_suspend(rcm_handle_t *, char *, id_t, timespec_t *,
79 			    uint_t, char **, rcm_info_t **);
80 static int		dump_resume(rcm_handle_t *, char *, id_t, uint_t,
81 			    char **, rcm_info_t **);
82 static int		dump_offline(rcm_handle_t *, char *, id_t, uint_t,
83 			    char **, rcm_info_t **);
84 static int		dump_online(rcm_handle_t *, char *, id_t, uint_t,
85 			    char **, rcm_info_t **);
86 static int		dump_remove(rcm_handle_t *, char *, id_t, uint_t,
87 			    char **, rcm_info_t **);
88 
89 static int		alloc_usage(char **, int);
90 static void		cache_insert(dump_conf_t *);
91 static dump_conf_t	*cache_lookup(char *);
92 static void		cache_remove(dump_conf_t *);
93 static dump_conf_t	*dump_conf_alloc(void);
94 static int		dump_configure(dump_conf_t *, char **);
95 static int		dump_relocate(dump_conf_t *, char **);
96 static void		free_cache(void);
97 static void		log_cmd_status(int);
98 static int		update_cache(rcm_handle_t *);
99 
100 static struct rcm_mod_ops dump_ops =
101 {
102 	RCM_MOD_OPS_VERSION,
103 	dump_register,
104 	dump_unregister,
105 	dump_getinfo,
106 	dump_suspend,
107 	dump_resume,
108 	dump_offline,
109 	dump_online,
110 	dump_remove,
111 	NULL,
112 	NULL,
113 	NULL
114 };
115 
116 struct rcm_mod_ops *
117 rcm_mod_init()
118 {
119 	return (&dump_ops);
120 }
121 
122 const char *
123 rcm_mod_info()
124 {
125 	return ("RCM Dump module 1.3");
126 }
127 
128 int
129 rcm_mod_fini()
130 {
131 	free_cache();
132 	(void) mutex_destroy(&cache_lock);
133 
134 	return (RCM_SUCCESS);
135 }
136 
137 static int
138 dump_register(rcm_handle_t *hdl)
139 {
140 	return (update_cache(hdl));
141 }
142 
143 static int
144 dump_unregister(rcm_handle_t *hdl)
145 {
146 	dump_conf_t	*dc;
147 
148 	(void) mutex_lock(&cache_lock);
149 	while ((dc = cache) != NULL) {
150 		cache = cache->next;
151 		(void) rcm_unregister_interest(hdl, dc->device, 0);
152 		free(dc);
153 	}
154 	(void) mutex_unlock(&cache_lock);
155 
156 	return (RCM_SUCCESS);
157 }
158 
159 /*ARGSUSED*/
160 static int
161 dump_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
162     char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent)
163 {
164 	dump_conf_t	*dc;
165 	int		conf_flags;
166 
167 	assert(rsrcname != NULL && infostr != NULL);
168 
169 	(void) mutex_lock(&cache_lock);
170 	if ((dc = cache_lookup(rsrcname)) == NULL) {
171 		(void) mutex_unlock(&cache_lock);
172 		rcm_log_message(RCM_ERROR, "unknown resource: %s\n",
173 		    rsrcname);
174 		return (RCM_FAILURE);
175 	}
176 	conf_flags = dc->conf_flags;
177 	(void) mutex_unlock(&cache_lock);
178 
179 	return ((alloc_usage(infostr, conf_flags) == 0) ?
180 	    RCM_SUCCESS : RCM_FAILURE);
181 }
182 
183 /*
184  * Relocate dump device to maintain availability during suspension.
185  * Fail request if unable to relocate.
186  */
187 /*ARGSUSED*/
188 static int
189 dump_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval,
190     uint_t flags, char **errstr, rcm_info_t **dependent)
191 {
192 	dump_conf_t	*dc;
193 	int		rv;
194 
195 	assert(rsrcname != NULL && errstr != NULL);
196 
197 	if (flags & RCM_QUERY)
198 		return (RCM_SUCCESS);
199 
200 	(void) mutex_lock(&cache_lock);
201 	if ((dc = cache_lookup(rsrcname)) == NULL) {
202 		(void) mutex_unlock(&cache_lock);
203 		return (RCM_SUCCESS);
204 	}
205 
206 	rv = dump_relocate(dc, errstr);
207 	(void) mutex_unlock(&cache_lock);
208 
209 	return (rv);
210 }
211 
212 /*ARGSUSED*/
213 static int
214 dump_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
215     char **errstr, rcm_info_t **dependent)
216 {
217 	dump_conf_t	*dc;
218 	int		rv;
219 
220 	assert(rsrcname != NULL && errstr != NULL);
221 
222 	(void) mutex_lock(&cache_lock);
223 	if ((dc = cache_lookup(rsrcname)) == NULL) {
224 		(void) mutex_unlock(&cache_lock);
225 		return (RCM_SUCCESS);
226 	}
227 
228 	rv = dump_configure(dc, errstr);
229 	(void) mutex_unlock(&cache_lock);
230 
231 	return (rv);
232 }
233 
234 /*
235  * By default, reject offline. If offline request is
236  * forced, attempt to relocate the dump device.
237  */
238 /*ARGSUSED*/
239 static int
240 dump_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
241     char **errstr, rcm_info_t **dependent)
242 {
243 	dump_conf_t	*dc;
244 	int		conf_flags;
245 	int		rv;
246 
247 	assert(rsrcname != NULL && errstr != NULL);
248 
249 	if ((flags & RCM_FORCE) && (flags & RCM_QUERY))
250 		return (RCM_SUCCESS);
251 
252 	(void) mutex_lock(&cache_lock);
253 	if ((dc = cache_lookup(rsrcname)) == NULL) {
254 		(void) mutex_unlock(&cache_lock);
255 		return (RCM_SUCCESS);
256 	}
257 
258 	if (flags & RCM_FORCE) {
259 		rv = dump_relocate(dc, errstr);
260 		(void) mutex_unlock(&cache_lock);
261 		return (rv);
262 	}
263 
264 	/* default */
265 	conf_flags = dc->conf_flags;
266 	(void) mutex_unlock(&cache_lock);
267 	(void) alloc_usage(errstr, conf_flags);
268 
269 	return (RCM_FAILURE);
270 }
271 
272 /*ARGSUSED*/
273 static int
274 dump_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
275     char  **errstr, rcm_info_t **dependent)
276 {
277 	dump_conf_t	*dc;
278 	int		rv;
279 
280 	assert(rsrcname != NULL && errstr != NULL);
281 
282 	(void) mutex_lock(&cache_lock);
283 	if ((dc = cache_lookup(rsrcname)) == NULL) {
284 		(void) mutex_unlock(&cache_lock);
285 		return (RCM_SUCCESS);
286 	}
287 
288 	rv = dump_configure(dc, errstr);
289 	(void) mutex_unlock(&cache_lock);
290 
291 	return (rv);
292 }
293 
294 /*ARGSUSED*/
295 static int
296 dump_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
297     char **errstr, rcm_info_t **dependent)
298 {
299 	dump_conf_t	*dc;
300 
301 	assert(rsrcname != NULL && errstr != NULL);
302 
303 	(void) mutex_lock(&cache_lock);
304 	if ((dc = cache_lookup(rsrcname)) == NULL) {
305 		(void) mutex_unlock(&cache_lock);
306 		return (RCM_SUCCESS);
307 	}
308 	cache_remove(dc);
309 	free(dc);
310 	(void) mutex_unlock(&cache_lock);
311 
312 	return (RCM_SUCCESS);
313 }
314 
315 /*
316  * For dedicated dump devices, invoke dumpadm(1M)
317  * to relocate dump to swap. For dump device on
318  * swap, this is a no-op as the RCM swap module
319  * will relocate by invoking swap(1M).
320  *
321  * Call with cache_lock held.
322  */
323 static int
324 dump_relocate(dump_conf_t *dc, char **errstr)
325 {
326 	int		stat;
327 
328 	/*
329 	 * This state may get out of sync for a dump device on swap,
330 	 * since we will will not know if the swap module succeeds.
331 	 * Worst case is we end up invoking dumpadm to configure
332 	 * the same device during a rollback.
333 	 */
334 	dc->cache_flags |= DUMP_CACHE_OFFLINED;
335 
336 	/* RCM swap module will handle non-dedicated */
337 	if (!(dc->conf_flags & DUMP_EXCL))
338 		return (RCM_SUCCESS);
339 
340 	rcm_log_message(RCM_TRACE1, "%s\n", DUMPADM_SWAP);
341 	if ((stat = rcm_exec_cmd(DUMPADM_SWAP)) != 0) {
342 		log_cmd_status(stat);
343 		*errstr = strdup(gettext("unable to relocate dump device"));
344 		dc->cache_flags &= ~DUMP_CACHE_OFFLINED;
345 		return (RCM_FAILURE);
346 	}
347 
348 	return (RCM_SUCCESS);
349 }
350 
351 /*
352  * (Re)Configure dump device.
353  * Call with cache_lock held.
354  */
355 static int
356 dump_configure(dump_conf_t *dc, char **errstr)
357 {
358 	char		cmd[sizeof (DUMPADM) + MAXPATHLEN];
359 	int		stat;
360 
361 	assert(dc != NULL && dc->device != NULL);
362 
363 	/* minor optimization */
364 	if (!(dc->cache_flags & DUMP_CACHE_OFFLINED))
365 		return (RCM_SUCCESS);
366 
367 	(void) snprintf(cmd, sizeof (cmd), "%s%s", DUMPADM, dc->device);
368 	rcm_log_message(RCM_TRACE1, "%s\n", cmd);
369 	if ((stat = rcm_exec_cmd(cmd)) != 0) {
370 		log_cmd_status(stat);
371 		*errstr = strdup(gettext("unable to configure dump device"));
372 		return (RCM_FAILURE);
373 	}
374 	dc->cache_flags &= ~DUMP_CACHE_OFFLINED;
375 
376 	return (RCM_SUCCESS);
377 }
378 
379 /*
380  * Returns current dump configuration
381  */
382 static dump_conf_t *
383 dump_conf_alloc(void)
384 {
385 	dump_conf_t	*dc;
386 	struct stat	sbuf;
387 	int		fd;
388 	char		*err;
389 
390 	if ((dc = calloc(1, sizeof (*dc))) == NULL) {
391 		rcm_log_message(RCM_ERROR, "calloc failure\n");
392 		return (NULL);
393 	}
394 
395 	if ((fd = open("/dev/dump", O_RDONLY)) == -1) {
396 		/*
397 		 * Suppress reporting if no logical link.
398 		 */
399 		if (stat("/dev/dump", &sbuf) == 0 &&
400 		    (fd = open("/dev/dump", O_RDONLY)) == -1) {
401 			rcm_log_message(RCM_ERROR,
402 			    "failed to open /dev/dump: %s\n",
403 			    ((err = strerror(errno)) == NULL) ? "" : err);
404 		}
405 
406 		if (fd == -1) {
407 			free(dc);
408 			return (NULL);
409 		}
410 	}
411 
412 	if (ioctl(fd, DIOCGETDEV, dc->device) == -1) {
413 		if (errno == ENODEV) {
414 			dc->device[0] = '\0';
415 		} else {
416 			rcm_log_message(RCM_ERROR, "ioctl: %s\n",
417 			    ((err = strerror(errno)) == NULL) ? "" : err);
418 			(void) close(fd);
419 			free(dc);
420 			return (NULL);
421 		}
422 	}
423 
424 	if (dc->device[0] != '\0')  {
425 		if ((dc->conf_flags = ioctl(fd, DIOCGETCONF, 0)) == -1) {
426 			rcm_log_message(RCM_ERROR, "ioctl: %s\n",
427 			    ((err = strerror(errno)) == NULL) ? "" : err);
428 			(void) close(fd);
429 			free(dc);
430 			return (NULL);
431 		}
432 	}
433 	(void) close(fd);
434 
435 	return (dc);
436 }
437 
438 static int
439 update_cache(rcm_handle_t *hdl)
440 {
441 	dump_conf_t	*ent, *curr_dump, *tmp;
442 	int		rv = RCM_SUCCESS;
443 
444 	if ((curr_dump = dump_conf_alloc()) == NULL)
445 		return (RCM_FAILURE);
446 
447 	(void) mutex_lock(&cache_lock);
448 
449 	/*
450 	 * pass 1 -  mark all current registrations stale
451 	 */
452 	for (ent = cache; ent != NULL; ent = ent->next) {
453 		ent->cache_flags |= DUMP_CACHE_STALE;
454 	}
455 
456 	/*
457 	 * update current dump conf
458 	 */
459 	if (curr_dump->device[0] == '\0') {
460 		free(curr_dump);
461 	} else if ((ent = cache_lookup(curr_dump->device)) != NULL) {
462 		ent->cache_flags &= ~DUMP_CACHE_STALE;
463 		ent->conf_flags = curr_dump->conf_flags;
464 		free(curr_dump);
465 	} else {
466 		curr_dump->cache_flags |= DUMP_CACHE_NEW;
467 		cache_insert(curr_dump);
468 	}
469 
470 	/*
471 	 * pass 2 - register, unregister, or no-op based on cache flags
472 	 */
473 	ent = cache;
474 	while (ent != NULL) {
475 		if (ent->cache_flags & DUMP_CACHE_OFFLINED) {
476 			ent = ent->next;
477 			continue;
478 		}
479 
480 		if (ent->cache_flags & DUMP_CACHE_STALE) {
481 			if (rcm_unregister_interest(hdl, ent->device, 0) !=
482 			    RCM_SUCCESS) {
483 				rcm_log_message(RCM_ERROR, "failed to "
484 				    "unregister %s\n", ent->device);
485 			}
486 			tmp = ent;
487 			ent = ent->next;
488 			cache_remove(tmp);
489 			free(tmp);
490 			continue;
491 		}
492 
493 		if (!(ent->cache_flags & DUMP_CACHE_NEW)) {
494 			ent = ent->next;
495 			continue;
496 		}
497 
498 		if (rcm_register_interest(hdl, ent->device, 0, NULL) !=
499 		    RCM_SUCCESS) {
500 			rcm_log_message(RCM_ERROR, "failed to register "
501 			    "%s\n", ent->device);
502 			rv = RCM_FAILURE;
503 		} else {
504 			rcm_log_message(RCM_DEBUG, "registered %s\n",
505 			    ent->device);
506 			ent->cache_flags &= ~DUMP_CACHE_NEW;
507 		}
508 		ent = ent->next;
509 	}
510 	(void) mutex_unlock(&cache_lock);
511 
512 	return (rv);
513 }
514 
515 /*
516  * Call with cache_lock held.
517  */
518 static dump_conf_t *
519 cache_lookup(char *rsrc)
520 {
521 	dump_conf_t	*dc;
522 
523 	for (dc = cache; dc != NULL; dc = dc->next) {
524 		if (strcmp(rsrc, dc->device) == 0) {
525 			return (dc);
526 		}
527 	}
528 	return (NULL);
529 }
530 
531 /*
532  * Link to front of list.
533  * Call with cache_lock held.
534  */
535 static void
536 cache_insert(dump_conf_t *ent)
537 {
538 	ent->next = cache;
539 	if (ent->next)
540 		ent->next->prev = ent;
541 	ent->prev = NULL;
542 	cache = ent;
543 }
544 
545 /*
546  * Call with cache_lock held.
547  */
548 static void
549 cache_remove(dump_conf_t *ent)
550 {
551 	if (ent->next != NULL) {
552 		ent->next->prev = ent->prev;
553 	}
554 	if (ent->prev != NULL) {
555 		ent->prev->next = ent->next;
556 	} else {
557 		cache = ent->next;
558 	}
559 	ent->next = NULL;
560 	ent->prev = NULL;
561 }
562 
563 static void
564 free_cache(void)
565 {
566 	dump_conf_t	*dc;
567 
568 	(void) mutex_lock(&cache_lock);
569 	while ((dc = cache) != NULL) {
570 		cache = cache->next;
571 		free(dc);
572 	}
573 	(void) mutex_unlock(&cache_lock);
574 }
575 
576 static int
577 alloc_usage(char **cpp, int conf_flags)
578 {
579 	/* simplifies message translation */
580 	if (conf_flags & DUMP_EXCL) {
581 		*cpp = strdup(gettext("dump device (dedicated)"));
582 	} else {
583 		*cpp = strdup(gettext("dump device (swap)"));
584 	}
585 
586 	if (*cpp == NULL) {
587 		rcm_log_message(RCM_ERROR, "strdup failure\n");
588 		return (-1);
589 	}
590 	return (0);
591 }
592 
593 static void
594 log_cmd_status(int stat)
595 {
596 	char	*err;
597 
598 	if (stat == -1) {
599 		rcm_log_message(RCM_ERROR, "wait: %s\n",
600 		    ((err =  strerror(errno)) == NULL) ? "" : err);
601 	} else if (WIFEXITED(stat)) {
602 		rcm_log_message(RCM_ERROR, "exit status: %d\n",
603 		    WEXITSTATUS(stat));
604 	} else {
605 		rcm_log_message(RCM_ERROR, "wait status: %d\n", stat);
606 	}
607 }
608