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