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