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