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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
23 */
24
25 #pragma ident "%Z%%M% %I% %E% SMI"
26
27 #include <librcm_impl.h>
28 #include "rcm_impl.h"
29
30 static int query(char **, int, const char *, int, pid_t, uint_t, timespec_t *,
31 int, rcm_info_t **, int *);
32 static void cancel_query(int, const char *, pid_t, uint_t, int);
33
34 /*
35 * The following ops are invoked when modules initiate librcm calls which
36 * require daemon processing. Cascaded RCM operations must come through
37 * this path.
38 */
39 librcm_ops_t rcm_ops = {
40 add_resource_client,
41 remove_resource_client,
42 get_resource_info,
43 process_resource_suspend,
44 notify_resource_resume,
45 process_resource_offline,
46 notify_resource_online,
47 notify_resource_remove,
48 request_capacity_change,
49 notify_capacity_change,
50 notify_resource_event,
51 get_resource_state
52 };
53
54 /*
55 * Process a request or a notification on a subtree
56 */
57 /*ARGSUSED2*/
58 static int
common_resource_op(int cmd,char * rsrcname,pid_t pid,uint_t flag,int seq_num,timespec_t * interval,nvlist_t * nvl,rcm_info_t ** info)59 common_resource_op(int cmd, char *rsrcname, pid_t pid, uint_t flag, int seq_num,
60 timespec_t *interval, nvlist_t *nvl, rcm_info_t **info)
61 {
62 int error;
63 rsrc_node_t *node;
64 tree_walk_arg_t arg;
65
66 /*
67 * Find the node (root of subtree) in the resource tree, invoke
68 * appropriate callbacks for all clients hanging off the subtree,
69 * and mark the subtree with the appropriate state.
70 *
71 * NOTE: It's possible the node doesn't exist, which means no RCM
72 * consumer registered for the resource. In this case we silently
73 * succeed.
74 */
75 error = rsrc_node_find(rsrcname, 0, &node);
76 if ((error == RCM_SUCCESS) && (node != NULL)) {
77 arg.flag = flag;
78 arg.info = info;
79 arg.seq_num = seq_num;
80 arg.interval = interval;
81 arg.nvl = nvl;
82 arg.cmd = cmd;
83
84 if ((cmd == CMD_NOTIFY_CHANGE) ||
85 (cmd == CMD_REQUEST_CHANGE) ||
86 (cmd == CMD_EVENT)) {
87 error = rsrc_client_action_list(node->users, cmd, &arg);
88 } else {
89 error = rsrc_tree_action(node, cmd, &arg);
90 }
91 } else if ((error == RCM_SUCCESS) && (flag & RCM_RETIRE_REQUEST)) {
92 /*
93 * No matching node, so no client. This means there
94 * is no constraint (RCM wise) on this retire. Return
95 * RCM_NO_CONSTRAINT to indicate this
96 */
97 rcm_log_message(RCM_TRACE1, "No client. Returning "
98 "RCM_NO_CONSTRAINT: %s\n", rsrcname);
99 error = RCM_NO_CONSTRAINT;
100 }
101
102 return (error);
103 }
104
105 /*
106 * When a resource is removed, notify all clients who registered for this
107 * particular resource.
108 */
109 int
notify_resource_remove(char ** rsrcnames,pid_t pid,uint_t flag,int seq_num,rcm_info_t ** info)110 notify_resource_remove(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
111 rcm_info_t **info)
112 {
113 int i;
114 int error;
115 int retval = RCM_SUCCESS;
116
117 for (i = 0; rsrcnames[i] != NULL; i++) {
118
119 rcm_log_message(RCM_TRACE2,
120 "notify_resource_remove(%s, %ld, 0x%x, %d)\n", rsrcnames[i],
121 pid, flag, seq_num);
122
123 /*
124 * Mark state as issuing removal notification. Return failure
125 * if no DR request for this node exists.
126 */
127 error = dr_req_update(rsrcnames[i], pid, flag,
128 RCM_STATE_REMOVING, seq_num, info);
129 if (error != RCM_SUCCESS) {
130 retval = error;
131 continue;
132 }
133
134 error = common_resource_op(CMD_REMOVE, rsrcnames[i], pid, flag,
135 seq_num, NULL, NULL, info);
136
137 /*
138 * delete the request entry from DR list
139 */
140 dr_req_remove(rsrcnames[i], flag);
141
142 if (error != RCM_SUCCESS)
143 retval = error;
144 }
145
146 return (retval);
147 }
148
149 /*
150 * Notify users that a resource has been resumed
151 */
152 int
notify_resource_resume(char ** rsrcnames,pid_t pid,uint_t flag,int seq_num,rcm_info_t ** info)153 notify_resource_resume(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
154 rcm_info_t **info)
155 {
156 int i;
157 int error;
158 rcm_info_t *state_info;
159 rcm_info_tuple_t *state_tuple;
160 int retval = RCM_SUCCESS;
161
162 for (i = 0; rsrcnames[i] != NULL; i++) {
163
164 state_info = NULL;
165 state_tuple = NULL;
166
167 /* Check resource state (was resource actually suspended?) */
168 if (get_resource_state(rsrcnames[i], pid, &state_info) ||
169 ((state_tuple = rcm_info_next(state_info, NULL)) == NULL) ||
170 (rcm_info_state(state_tuple) == RCM_STATE_SUSPEND))
171 flag |= RCM_SUSPENDED;
172 if (state_info)
173 rcm_free_info(state_info);
174
175 rcm_log_message(RCM_TRACE2,
176 "notify_resource_resume(%s, %ld, 0x%x, %d)\n",
177 rsrcnames[i], pid, flag, seq_num);
178
179 /*
180 * Mark state as sending resumption notifications
181 */
182 error = dr_req_update(rsrcnames[i], pid, flag,
183 RCM_STATE_RESUMING, seq_num, info);
184 if (error != RCM_SUCCESS) {
185 retval = error;
186 continue;
187 }
188
189 error = common_resource_op(CMD_RESUME, rsrcnames[i], pid, flag,
190 seq_num, NULL, NULL, info);
191
192 dr_req_remove(rsrcnames[i], flag);
193
194 if (error != RCM_SUCCESS)
195 retval = error;
196 }
197
198 return (retval);
199 }
200
201 /*
202 * Notify users that an offlined device is again available
203 */
204 int
notify_resource_online(char ** rsrcnames,pid_t pid,uint_t flag,int seq_num,rcm_info_t ** info)205 notify_resource_online(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
206 rcm_info_t **info)
207 {
208 int i;
209 int error;
210 int retval = RCM_SUCCESS;
211
212 for (i = 0; rsrcnames[i] != NULL; i++) {
213
214 rcm_log_message(RCM_TRACE2,
215 "notify_resource_online(%s, %ld, 0x%x, %d)\n",
216 rsrcnames[i], pid, flag, seq_num);
217
218 /*
219 * Mark state as sending onlining notifications
220 */
221 error = dr_req_update(rsrcnames[i], pid, flag,
222 RCM_STATE_ONLINING, seq_num, info);
223 if (error != RCM_SUCCESS) {
224 retval = error;
225 continue;
226 }
227
228 error = common_resource_op(CMD_ONLINE, rsrcnames[i], pid, flag,
229 seq_num, NULL, NULL, info);
230
231 dr_req_remove(rsrcnames[i], flag);
232
233 if (error != RCM_SUCCESS)
234 retval = error;
235 }
236
237 return (retval);
238 }
239
240 /*
241 * For offline and suspend, need to get the logic correct here. There are
242 * several cases:
243 *
244 * 1. It is a door call and RCM_QUERY is not set:
245 * run a QUERY; if that succeeds, run the operation.
246 *
247 * 2. It is a door call and RCM_QUERY is set:
248 * run the QUERY only.
249 *
250 * 3. It is not a door call:
251 * run the call, but look at the flag to see if the
252 * lock should be kept.
253 */
254
255 /*
256 * Request permission to suspend a resource
257 */
258 int
process_resource_suspend(char ** rsrcnames,pid_t pid,uint_t flag,int seq_num,timespec_t * interval,rcm_info_t ** info)259 process_resource_suspend(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
260 timespec_t *interval, rcm_info_t **info)
261 {
262 int i;
263 int error = RCM_SUCCESS;
264 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
265
266 /*
267 * Query the operation first. The return value of the query indicates
268 * if the operation should proceed and be implemented.
269 */
270 if (query(rsrcnames, CMD_SUSPEND, "suspend", RCM_STATE_SUSPEND_QUERYING,
271 pid, flag, interval, seq_num, info, &error) == 0) {
272 return (error);
273 }
274
275 /*
276 * Implement the operation.
277 */
278 for (i = 0; rsrcnames[i] != NULL; i++) {
279
280 /* Update the lock from a query state to the suspending state */
281 if ((error = dr_req_update(rsrcnames[i], pid, flag,
282 RCM_STATE_SUSPENDING, seq_num, info)) != RCM_SUCCESS) {
283
284 rcm_log_message(RCM_DEBUG,
285 "suspend %s denied with error %d\n", rsrcnames[i],
286 error);
287
288 /*
289 * When called from a module, don't return EAGAIN.
290 * This is to avoid recursion if module always retries.
291 */
292 if (!is_doorcall && error == EAGAIN) {
293 return (RCM_CONFLICT);
294 }
295
296 return (error);
297 }
298
299 /* Actually suspend the resource */
300 error = common_resource_op(CMD_SUSPEND, rsrcnames[i], pid,
301 flag, seq_num, interval, NULL, info);
302 if (error != RCM_SUCCESS) {
303 (void) dr_req_update(rsrcnames[i], pid, flag,
304 RCM_STATE_SUSPEND_FAIL, seq_num, info);
305 rcm_log_message(RCM_DEBUG,
306 "suspend tree failed for %s\n", rsrcnames[i]);
307 return (error);
308 }
309
310 rcm_log_message(RCM_TRACE3, "suspend tree succeeded for %s\n",
311 rsrcnames[i]);
312
313 /* Update the lock for the successful suspend */
314 (void) dr_req_update(rsrcnames[i], pid, flag,
315 RCM_STATE_SUSPEND, seq_num, info);
316 }
317
318 return (RCM_SUCCESS);
319 }
320
321 /*
322 * Process a device removal request, reply is needed
323 */
324 int
process_resource_offline(char ** rsrcnames,pid_t pid,uint_t flag,int seq_num,rcm_info_t ** info)325 process_resource_offline(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
326 rcm_info_t **info)
327 {
328 int i;
329 int error = RCM_SUCCESS;
330 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
331
332 /*
333 * Query the operation first. The return value of the query indicates
334 * if the operation should proceed and be implemented.
335 */
336 if (query(rsrcnames, CMD_OFFLINE, "offline", RCM_STATE_OFFLINE_QUERYING,
337 pid, flag, NULL, seq_num, info, &error) == 0) {
338 return (error);
339 }
340
341 /*
342 * Implement the operation.
343 */
344 for (i = 0; rsrcnames[i] != NULL; i++) {
345
346 error = dr_req_update(rsrcnames[i], pid, flag,
347 RCM_STATE_OFFLINING, seq_num, info);
348 if (error != RCM_SUCCESS) {
349 rcm_log_message(RCM_DEBUG,
350 "offline %s denied with error %d\n", rsrcnames[i],
351 error);
352
353 /*
354 * When called from a module, don't return EAGAIN.
355 * This is to avoid recursion if module always retries.
356 */
357 if (!is_doorcall && error == EAGAIN) {
358 return (RCM_CONFLICT);
359 }
360
361 return (error);
362 }
363
364 /* Actually offline the resource */
365 error = common_resource_op(CMD_OFFLINE, rsrcnames[i], pid,
366 flag, seq_num, NULL, NULL, info);
367 if (error != RCM_SUCCESS) {
368 (void) dr_req_update(rsrcnames[i], pid, flag,
369 RCM_STATE_OFFLINE_FAIL, seq_num, info);
370 rcm_log_message(RCM_DEBUG,
371 "offline tree failed for %s\n", rsrcnames[i]);
372 return (error);
373 }
374
375 rcm_log_message(RCM_TRACE3, "offline tree succeeded for %s\n",
376 rsrcnames[i]);
377
378 /* Update the lock for the successful offline */
379 (void) dr_req_update(rsrcnames[i], pid, flag,
380 RCM_STATE_OFFLINE, seq_num, info);
381 }
382
383 return (RCM_SUCCESS);
384 }
385
386 /*
387 * Add a resource client who wishes to interpose on DR, events, or capacity.
388 * Reply needed.
389 */
390 int
add_resource_client(char * modname,char * rsrcname,pid_t pid,uint_t flag,rcm_info_t ** infop)391 add_resource_client(char *modname, char *rsrcname, pid_t pid, uint_t flag,
392 rcm_info_t **infop)
393 {
394 int error = RCM_SUCCESS;
395 client_t *user = NULL;
396 rsrc_node_t *node = NULL;
397 rcm_info_t *info = NULL;
398
399 rcm_log_message(RCM_TRACE2,
400 "add_resource_client(%s, %s, %ld, 0x%x)\n",
401 modname, rsrcname, pid, flag);
402
403 if (strcmp(rsrcname, "/") == 0) {
404 /*
405 * No need to register for / because it will never go away.
406 */
407 rcm_log_message(RCM_INFO, gettext(
408 "registering for / by %s has been turned into a no-op\n"),
409 modname);
410 return (RCM_SUCCESS);
411 }
412
413 /*
414 * Hold the rcm_req_lock so no dr request may come in while the
415 * registration is in progress.
416 */
417 (void) mutex_lock(&rcm_req_lock);
418
419 /*
420 * Test if the requested registration is a noop, and return EALREADY
421 * if it is.
422 */
423 error = rsrc_node_find(rsrcname, RSRC_NODE_CREATE, &node);
424 if ((error != RCM_SUCCESS) || (node == NULL)) {
425 (void) mutex_unlock(&rcm_req_lock);
426 return (RCM_FAILURE);
427 }
428
429 user = rsrc_client_find(modname, pid, &node->users);
430 if ((user != NULL) &&
431 ((user->flag & (flag & RCM_REGISTER_MASK)) != 0)) {
432 (void) mutex_unlock(&rcm_req_lock);
433 if ((flag & RCM_REGISTER_DR) &&
434 (user->state == RCM_STATE_REMOVE)) {
435 user->state = RCM_STATE_ONLINE;
436 return (RCM_SUCCESS);
437 }
438 return (EALREADY);
439 }
440
441 /* If adding a new DR registration, reject if the resource is locked */
442 if (flag & RCM_REGISTER_DR) {
443
444 if (rsrc_check_lock_conflicts(rsrcname, flag, LOCK_FOR_USE,
445 &info) != RCM_SUCCESS) {
446 /*
447 * The resource is being DR'ed, so return failure
448 */
449 (void) mutex_unlock(&rcm_req_lock);
450
451 /*
452 * If caller doesn't care about info, free it
453 */
454 if (infop)
455 *infop = info;
456 else
457 rcm_free_info(info);
458
459 return (RCM_CONFLICT);
460 }
461 }
462
463 /* The registration is new and allowable, so add it */
464 error = rsrc_node_add_user(node, rsrcname, modname, pid, flag);
465 (void) mutex_unlock(&rcm_req_lock);
466
467 return (error);
468 }
469
470 /*
471 * Remove a resource client, who no longer wishes to interpose on either
472 * DR, events, or capacity.
473 */
474 int
remove_resource_client(char * modname,char * rsrcname,pid_t pid,uint_t flag)475 remove_resource_client(char *modname, char *rsrcname, pid_t pid, uint_t flag)
476 {
477 int error;
478 rsrc_node_t *node;
479
480 rcm_log_message(RCM_TRACE2,
481 "remove_resource_client(%s, %s, %ld, 0x%x)\n",
482 modname, rsrcname, pid, flag);
483
484 /*
485 * Allow resource client to leave anytime, assume client knows what
486 * it is trying to do.
487 */
488 error = rsrc_node_find(rsrcname, 0, &node);
489 if ((error != RCM_SUCCESS) || (node == NULL)) {
490 rcm_log_message(RCM_WARNING,
491 gettext("resource %s not found\n"), rsrcname);
492 return (ENOENT);
493 }
494
495 return (rsrc_node_remove_user(node, modname, pid, flag));
496 }
497
498 /*
499 * Reply is needed
500 */
501 int
get_resource_info(char ** rsrcnames,uint_t flag,int seq_num,rcm_info_t ** info)502 get_resource_info(char **rsrcnames, uint_t flag, int seq_num, rcm_info_t **info)
503 {
504 int rv = RCM_SUCCESS;
505
506 if (flag & RCM_DR_OPERATION) {
507 *info = rsrc_dr_info();
508 } else if (flag & RCM_MOD_INFO) {
509 *info = rsrc_mod_info();
510 } else {
511 rv = rsrc_usage_info(rsrcnames, flag, seq_num, info);
512 }
513
514 return (rv);
515 }
516
517 int
notify_resource_event(char * rsrcname,id_t pid,uint_t flag,int seq_num,nvlist_t * event_data,rcm_info_t ** info)518 notify_resource_event(char *rsrcname, id_t pid, uint_t flag, int seq_num,
519 nvlist_t *event_data, rcm_info_t **info)
520 {
521 int error;
522
523 assert(flag == 0);
524
525 rcm_log_message(RCM_TRACE2, "notify_resource_event(%s, %ld, 0x%x)\n",
526 rsrcname, pid, flag);
527
528 error = common_resource_op(CMD_EVENT, rsrcname, pid, flag, seq_num,
529 NULL, event_data, info);
530
531 return (error);
532 }
533
534 int
request_capacity_change(char * rsrcname,id_t pid,uint_t flag,int seq_num,nvlist_t * nvl,rcm_info_t ** info)535 request_capacity_change(char *rsrcname, id_t pid, uint_t flag, int seq_num,
536 nvlist_t *nvl, rcm_info_t **info)
537 {
538 int error;
539 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
540
541 rcm_log_message(RCM_TRACE2,
542 "request_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname, pid,
543 flag, seq_num);
544
545 if (is_doorcall || (flag & RCM_QUERY)) {
546
547 error = common_resource_op(CMD_REQUEST_CHANGE, rsrcname, pid,
548 flag | RCM_QUERY, seq_num, NULL, nvl, info);
549
550 if (error != RCM_SUCCESS) {
551 rcm_log_message(RCM_DEBUG,
552 "request state change query denied\n");
553 return (error);
554 }
555 }
556
557 if (flag & RCM_QUERY)
558 return (RCM_SUCCESS);
559
560 error = common_resource_op(CMD_REQUEST_CHANGE, rsrcname, pid, flag,
561 seq_num, NULL, nvl, info);
562
563 if (error != RCM_SUCCESS) {
564 rcm_log_message(RCM_DEBUG, "request state change failed\n");
565 return (RCM_FAILURE);
566 }
567
568 rcm_log_message(RCM_TRACE3, "request state change succeeded\n");
569
570 return (error);
571 }
572
573 int
notify_capacity_change(char * rsrcname,id_t pid,uint_t flag,int seq_num,nvlist_t * nvl,rcm_info_t ** info)574 notify_capacity_change(char *rsrcname, id_t pid, uint_t flag, int seq_num,
575 nvlist_t *nvl, rcm_info_t **info)
576 {
577 int error;
578
579 rcm_log_message(RCM_TRACE2,
580 "notify_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname, pid,
581 flag, seq_num);
582
583 error = common_resource_op(CMD_NOTIFY_CHANGE, rsrcname, pid, flag,
584 seq_num, NULL, nvl, info);
585
586 if (error != RCM_SUCCESS) {
587 rcm_log_message(RCM_DEBUG, "notify state change failed\n");
588 return (RCM_FAILURE);
589 }
590
591 rcm_log_message(RCM_TRACE3, "notify state change succeeded\n");
592
593 return (error);
594 }
595
596 int
get_resource_state(char * rsrcname,pid_t pid,rcm_info_t ** info)597 get_resource_state(char *rsrcname, pid_t pid, rcm_info_t **info)
598 {
599 int error;
600 int state;
601 char *s;
602 char *resolved;
603 rcm_info_t *dr_info = NULL;
604 rcm_info_tuple_t *dr_info_tuple = NULL;
605 rsrc_node_t *node;
606 client_t *client;
607 char *state_info = gettext("State of resource");
608
609 rcm_log_message(RCM_TRACE2, "get_resource_state(%s, %ld)\n",
610 rsrcname, pid);
611
612 /*
613 * Check for locks, first.
614 */
615 dr_info = rsrc_dr_info();
616 if (dr_info) {
617 state = RCM_STATE_UNKNOWN;
618 if ((resolved = resolve_name(rsrcname)) == NULL)
619 return (RCM_FAILURE);
620 while (dr_info_tuple = rcm_info_next(dr_info, dr_info_tuple)) {
621 s = (char *)rcm_info_rsrc(dr_info_tuple);
622 if (s && (strcmp(resolved, s) == 0)) {
623 state = rcm_info_state(dr_info_tuple);
624 break;
625 }
626 }
627 free(resolved);
628 rcm_free_info(dr_info);
629 if (state != RCM_STATE_UNKNOWN) {
630 rcm_log_message(RCM_TRACE2,
631 "get_resource_state(%s)=%d\n", rsrcname, state);
632 add_busy_rsrc_to_list(rsrcname, pid, state, 0, NULL,
633 (char *)state_info, NULL, NULL, info);
634 return (RCM_SUCCESS);
635 }
636 }
637
638 /*
639 * No locks, so look for client states in the resource tree.
640 *
641 * NOTE: It's possible the node doesn't exist, which means no RCM
642 * consumer registered for the resource. In this case we silently
643 * succeed.
644 */
645 error = rsrc_node_find(rsrcname, 0, &node);
646 state = RCM_STATE_ONLINE;
647
648 if ((error == RCM_SUCCESS) && (node != NULL)) {
649 for (client = node->users; client; client = client->next) {
650 if (client->state == RCM_STATE_OFFLINE_FAIL ||
651 client->state == RCM_STATE_OFFLINE_QUERY_FAIL ||
652 client->state == RCM_STATE_SUSPEND_FAIL ||
653 client->state == RCM_STATE_SUSPEND_QUERY_FAIL) {
654 state = client->state;
655 break;
656 }
657
658 if (client->state != RCM_STATE_ONLINE &&
659 client->state != RCM_STATE_REMOVE)
660 state = client->state;
661 }
662 }
663
664 if (error == RCM_SUCCESS) {
665 rcm_log_message(RCM_TRACE2, "get_resource_state(%s)=%d\n",
666 rsrcname, state);
667 add_busy_rsrc_to_list(rsrcname, pid, state, 0, NULL,
668 (char *)state_info, NULL, NULL, info);
669 }
670
671 return (error);
672 }
673
674 /*
675 * Perform a query of an offline or suspend.
676 *
677 * The return value of this function indicates whether the operation should
678 * be implemented (0 == No, 1 == Yes). Note that locks and client state
679 * changes will only persist if the caller is going to implement the operation.
680 */
681 static int
query(char ** rsrcnames,int cmd,const char * opname,int querystate,pid_t pid,uint_t flag,timespec_t * interval,int seq_num,rcm_info_t ** info,int * errorp)682 query(char **rsrcnames, int cmd, const char *opname, int querystate, pid_t pid,
683 uint_t flag, timespec_t *interval, int seq_num, rcm_info_t **info,
684 int *errorp)
685 {
686 int i;
687 int error;
688 int final_error;
689 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
690
691 /* Only query for door calls, or when the RCM_QUERY flag is set */
692 if ((is_doorcall == 0) && ((flag & RCM_QUERY) == 0)) {
693 return (1);
694 }
695
696 /* Lock all the resources. Fail the query in the case of a conflict. */
697 for (i = 0; rsrcnames[i] != NULL; i++) {
698
699 rcm_log_message(RCM_TRACE2,
700 "process_resource_%s(%s, %ld, 0x%x, %d)\n",
701 opname, rsrcnames[i], pid, flag, seq_num);
702
703 error = dr_req_add(rsrcnames[i], pid, flag, querystate, seq_num,
704 NULL, info);
705
706 /* The query goes no further if a resource cannot be locked */
707 if (error != RCM_SUCCESS) {
708
709 rcm_log_message(RCM_DEBUG,
710 "%s query %s defined with error %d\n",
711 opname, rsrcnames[i], error);
712
713 /*
714 * Replace EAGAIN with RCM_CONFLICT in the case of
715 * module callbacks; to avoid modules from trying
716 * again infinitely.
717 */
718 if ((is_doorcall == 0) && (error == EAGAIN)) {
719 error = RCM_CONFLICT;
720 }
721
722 goto finished;
723 }
724 }
725
726 /*
727 * All the resources were locked above, so use common_resource_op()
728 * to pass the query on to the clients. Accumulate the overall error
729 * value in 'final_error', before transferring it to 'error' at the end.
730 */
731 for (final_error = RCM_SUCCESS, i = 0; rsrcnames[i] != NULL; i++) {
732
733 /* Log the query (for tracing purposes). */
734 rcm_log_message(RCM_TRACE2, "querying resource %s\n",
735 rsrcnames[i]);
736
737 /* Query the resource's clients through common_resource_op(). */
738 error = common_resource_op(cmd, rsrcnames[i], pid,
739 flag | RCM_QUERY, seq_num, interval, NULL, info);
740
741 /*
742 * If a query fails, don't stop iterating through the loop.
743 * Just ensure that 'final_error' is set (if not already),
744 * log the error, and continue looping.
745 *
746 * In the case of a user who manually intervenes and retries
747 * the operation, this will maximize the extent of the query
748 * so that they experience fewer such iterations overall.
749 */
750 if (error != RCM_SUCCESS) {
751
752 /* Log each query that failed along the way */
753 rcm_log_message(RCM_DEBUG, "%s %s query denied\n",
754 opname, rsrcnames[i]);
755
756 if (final_error != RCM_FAILURE) {
757 final_error = error;
758 }
759 }
760 }
761 error = final_error;
762
763 /*
764 * Tell the calling function not to proceed any further with the
765 * implementation phase of the operation if the query failed, or
766 * if the user's intent was to only query the operation.
767 */
768 finished:
769 if ((error != RCM_SUCCESS) || ((flag & RCM_QUERY) != 0)) {
770
771 /*
772 * Since the operation won't be implemented, cancel the
773 * query (unlock resources and reverse client state changes).
774 *
775 * The cancellation routine cleans up everything for the entire
776 * operation, and thus it should only be called from the very
777 * root of the operation (e.g. when 'is_doorcall' is TRUE).
778 */
779 if (is_doorcall != 0) {
780 cancel_query(cmd, opname, pid, flag, seq_num);
781 }
782
783 *errorp = error;
784 return (0);
785 }
786
787 /* Otherwise, tell the caller to proceed with the implementation. */
788 *errorp = RCM_SUCCESS;
789 return (1);
790 }
791
792 /*
793 * Implementation of a query cancellation.
794 *
795 * The full scope of the query is already noted, so the scope of the operation
796 * does not need to be expanded in the same recursive manner that was used for
797 * the query itself. (Clients don't have to be called to cross namespaces.)
798 * Instead, the locks added to the DR request list during the query are scanned.
799 */
800 static void
cancel_query(int cmd,const char * opname,pid_t pid,uint_t flag,int seq_num)801 cancel_query(int cmd, const char *opname, pid_t pid, uint_t flag, int seq_num)
802 {
803 char rsrc[MAXPATHLEN];
804
805 /*
806 * Find every lock in the DR request list that is a part of this
807 * sequence. Call common_resource_op() with the QUERY_CANCEL flag to
808 * cancel each sub-operation, and then remove each lock from the list.
809 *
810 * The 'rsrc' buffer is required to retrieve the 'device' fields of
811 * matching DR request list entries in a way that's multi-thread safe.
812 */
813 while (dr_req_lookup(seq_num, rsrc) == RCM_SUCCESS) {
814
815 rcm_log_message(RCM_TRACE2, "%s query %s cancelled\n",
816 opname, rsrc);
817
818 (void) common_resource_op(cmd, rsrc, pid,
819 flag | RCM_QUERY | RCM_QUERY_CANCEL, seq_num, NULL, NULL,
820 NULL);
821
822 (void) dr_req_remove(rsrc, flag);
823 }
824 }
825