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