Print this page
10703 smatch unreachable code checking needs reworking
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/contract/device.c
+++ new/usr/src/uts/common/contract/device.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 + * Copyright 2019 Joyent, Inc.
23 24 */
24 25
25 26 #include <sys/mutex.h>
26 27 #include <sys/debug.h>
27 28 #include <sys/types.h>
28 29 #include <sys/param.h>
29 30 #include <sys/kmem.h>
30 31 #include <sys/thread.h>
31 32 #include <sys/id_space.h>
32 33 #include <sys/avl.h>
33 34 #include <sys/list.h>
34 35 #include <sys/sysmacros.h>
35 36 #include <sys/proc.h>
36 37 #include <sys/contract.h>
37 38 #include <sys/contract_impl.h>
38 39 #include <sys/contract/device.h>
39 40 #include <sys/contract/device_impl.h>
40 41 #include <sys/cmn_err.h>
41 42 #include <sys/nvpair.h>
42 43 #include <sys/policy.h>
43 44 #include <sys/ddi_impldefs.h>
44 45 #include <sys/ddi_implfuncs.h>
45 46 #include <sys/systm.h>
46 47 #include <sys/stat.h>
47 48 #include <sys/sunddi.h>
48 49 #include <sys/esunddi.h>
49 50 #include <sys/ddi.h>
50 51 #include <sys/fs/dv_node.h>
51 52 #include <sys/sunndi.h>
52 53 #undef ct_lock /* needed because clnt.h defines ct_lock as a macro */
53 54
54 55 /*
55 56 * Device Contracts
56 57 * -----------------
57 58 * This file contains the core code for the device contracts framework.
58 59 * A device contract is an agreement or a contract between a process and
59 60 * the kernel regarding the state of the device. A device contract may be
60 61 * created when a relationship is formed between a device and a process
61 62 * i.e. at open(2) time, or it may be created at some point after the device
62 63 * has been opened. A device contract once formed may be broken by either party.
63 64 * A device contract can be broken by the process by an explicit abandon of the
64 65 * contract or by an implicit abandon when the process exits. A device contract
65 66 * can be broken by the kernel either asynchronously (without negotiation) or
66 67 * synchronously (with negotiation). Exactly which happens depends on the device
67 68 * state transition. The following state diagram shows the transitions between
68 69 * device states. Only device state transitions currently supported by device
69 70 * contracts is shown.
70 71 *
71 72 * <-- A -->
72 73 * /-----------------> DEGRADED
73 74 * | |
74 75 * | |
75 76 * | | S
76 77 * | | |
77 78 * | | v
78 79 * v S --> v
79 80 * ONLINE ------------> OFFLINE
80 81 *
81 82 *
82 83 * In the figure above, the arrows indicate the direction of transition. The
83 84 * letter S refers to transitions which are inherently synchronous i.e.
84 85 * require negotiation and the letter A indicates transitions which are
85 86 * asynchronous i.e. are done without contract negotiations. A good example
86 87 * of a synchronous transition is the ONLINE -> OFFLINE transition. This
87 88 * transition cannot happen as long as there are consumers which have the
88 89 * device open. Thus some form of negotiation needs to happen between the
89 90 * consumers and the kernel to ensure that consumers either close devices
90 91 * or disallow the move to OFFLINE. Certain other transitions such as
91 92 * ONLINE --> DEGRADED for example, are inherently asynchronous i.e.
92 93 * non-negotiable. A device that suffers a fault that degrades its
93 94 * capabilities will become degraded irrespective of what consumers it has,
94 95 * so a negotiation in this case is pointless.
95 96 *
96 97 * The following device states are currently defined for device contracts:
97 98 *
98 99 * CT_DEV_EV_ONLINE
99 100 * The device is online and functioning normally
100 101 * CT_DEV_EV_DEGRADED
101 102 * The device is online but is functioning in a degraded capacity
102 103 * CT_DEV_EV_OFFLINE
103 104 * The device is offline and is no longer configured
104 105 *
105 106 * A typical consumer of device contracts starts out with a contract
106 107 * template and adds terms to that template. These include the
107 108 * "acceptable set" (A-set) term, which is a bitset of device states which
108 109 * are guaranteed by the contract. If the device moves out of a state in
109 110 * the A-set, the contract is broken. The breaking of the contract can
110 111 * be asynchronous in which case a critical contract event is sent to the
111 112 * contract holder but no negotiations take place. If the breaking of the
112 113 * contract is synchronous, negotations are opened between the affected
113 114 * consumer and the kernel. The kernel does this by sending a critical
114 115 * event to the consumer with the CTE_NEG flag set indicating that this
115 116 * is a negotiation event. The consumer can accept this change by sending
116 117 * a ACK message to the kernel. Alternatively, if it has the necessary
117 118 * privileges, it can send a NACK message to the kernel which will block
118 119 * the device state change. To NACK a negotiable event, a process must
119 120 * have the {PRIV_SYS_DEVICES} privilege asserted in its effective set.
120 121 *
121 122 * Other terms include the "minor path" term, specified explicitly if the
122 123 * contract is not being created at open(2) time or specified implicitly
123 124 * if the contract is being created at open time via an activated template.
124 125 *
125 126 * A contract event is sent on any state change to which the contract
126 127 * owner has subscribed via the informative or critical event sets. Only
127 128 * critical events are guaranteed to be delivered. Since all device state
128 129 * changes are controlled by the kernel and cannot be arbitrarily generated
129 130 * by a non-privileged user, the {PRIV_CONTRACT_EVENT} privilege does not
130 131 * need to be asserted in a process's effective set to designate an event as
131 132 * critical. To ensure privacy, a process must either have the same effective
132 133 * userid as the contract holder or have the {PRIV_CONTRACT_OBSERVER} privilege
133 134 * asserted in its effective set in order to observe device contract events
134 135 * off the device contract type specific endpoint.
135 136 *
136 137 * Yet another term available with device contracts is the "non-negotiable"
137 138 * term. This term is used to pre-specify a NACK to any contract negotiation.
138 139 * This term is ignored for asynchronous state changes. For example, a
139 140 * provcess may have the A-set {ONLINE|DEGRADED} and make the contract
140 141 * non-negotiable. In this case, the device contract framework assumes a
141 142 * NACK for any transition to OFFLINE and blocks the offline. If the A-set
142 143 * is {ONLINE} and the non-negotiable term is set, transitions to OFFLINE
143 144 * are NACKed but transitions to DEGRADE succeed.
144 145 *
145 146 * The OFFLINE negotiation (if OFFLINE state is not in the A-set for a contract)
146 147 * happens just before the I/O framework attempts to offline a device
147 148 * (i.e. detach a device and set the offline flag so that it cannot be
148 149 * reattached). A device contract holder is expected to either NACK the offline
149 150 * (if privileged) or release the device and allow the offline to proceed.
150 151 *
151 152 * The DEGRADE contract event (if DEGRADE is not in the A-set for a contract)
152 153 * is generated just before the I/O framework transitions the device state
153 154 * to "degraded" (i.e. DEVI_DEVICE_DEGRADED in I/O framework terminology).
154 155 *
155 156 * The contract holder is expected to ACK or NACK a negotiation event
156 157 * within a certain period of time. If the ACK/NACK is not received
157 158 * within the timeout period, the device contract framework will behave
158 159 * as if the contract does not exist and will proceed with the event.
159 160 *
160 161 * Unlike a process contract a device contract does not need to exist
161 162 * once it is abandoned, since it does not define a fault boundary. It
162 163 * merely represents an agreement between a process and the kernel
163 164 * regarding the state of the device. Once the process has abandoned
164 165 * the contract (either implicitly via a process exit or explicitly)
165 166 * the kernel has no reason to retain the contract. As a result
166 167 * device contracts are neither inheritable nor need to exist in an
167 168 * orphan state.
168 169 *
169 170 * A device unlike a process may exist in multiple contracts and has
170 171 * a "life" outside a device contract. A device unlike a process
171 172 * may exist without an associated contract. Unlike a process contract
172 173 * a device contract may be formed after a binding relationship is
173 174 * formed between a process and a device.
174 175 *
175 176 * IMPLEMENTATION NOTES
176 177 * ====================
177 178 * DATA STRUCTURES
178 179 * ----------------
179 180 * The heart of the device contracts implementation is the device contract
180 181 * private cont_device_t (or ctd for short) data structure. It encapsulates
181 182 * the generic contract_t data structure and has a number of private
182 183 * fields.
183 184 * These include:
184 185 * cond_minor: The minor device that is the subject of the contract
185 186 * cond_aset: The bitset of states which are guaranteed by the
186 187 * contract
187 188 * cond_noneg: If set, indicates that the result of negotiation has
188 189 * been predefined to be a NACK
189 190 * In addition, there are other device identifiers such the devinfo node,
190 191 * dev_t and spec_type of the minor node. There are also a few fields that
191 192 * are used during negotiation to maintain state. See
192 193 * uts/common/sys/contract/device_impl.h
193 194 * for details.
194 195 * The ctd structure represents the device private part of a contract of
195 196 * type "device"
196 197 *
197 198 * Another data structure used by device contracts is ctmpl_device. It is
198 199 * the device contracts private part of the contract template structure. It
199 200 * encapsulates the generic template structure "ct_template_t" and includes
200 201 * the following device contract specific fields
201 202 * ctd_aset: The bitset of states that should be guaranteed by a
202 203 * contract
203 204 * ctd_noneg: If set, indicates that contract should NACK a
204 205 * negotiation
205 206 * ctd_minor: The devfs_path (without the /devices prefix) of the
206 207 * minor node that is the subject of the contract.
207 208 *
208 209 * ALGORITHMS
209 210 * ---------
210 211 * There are three sets of routines in this file
211 212 * Template related routines
212 213 * -------------------------
213 214 * These routines provide support for template related operations initated
214 215 * via the generic template operations. These include routines that dup
215 216 * a template, free it, and set various terms in the template
216 217 * (such as the minor node path, the acceptable state set (or A-set)
217 218 * and the non-negotiable term) as well as a routine to query the
218 219 * device specific portion of the template for the abovementioned terms.
219 220 * There is also a routine to create (ctmpl_device_create) that is used to
220 221 * create a contract from a template. This routine calls (after initial
221 222 * setup) the common function used to create a device contract
222 223 * (contract_device_create).
223 224 *
224 225 * core device contract implementation
225 226 * ----------------------------------
226 227 * These routines support the generic contract framework to provide
227 228 * functionality that allows contracts to be created, managed and
228 229 * destroyed. The contract_device_create() routine is a routine used
229 230 * to create a contract from a template (either via an explicit create
230 231 * operation on a template or implicitly via an open with an
231 232 * activated template.). The contract_device_free() routine assists
232 233 * in freeing the device contract specific parts. There are routines
233 234 * used to abandon (contract_device_abandon) a device contract as well
234 235 * as a routine to destroy (which despite its name does not destroy,
235 236 * it only moves a contract to a dead state) a contract.
236 237 * There is also a routine to return status information about a
237 238 * contract - the level of detail depends on what is requested by the
238 239 * user. A value of CTD_FIXED only returns fixed length fields such
239 240 * as the A-set, state of device and value of the "noneg" term. If
240 241 * CTD_ALL is specified, the minor node path is returned as well.
241 242 *
242 243 * In addition there are interfaces (contract_device_ack/nack) which
243 244 * are used to support negotiation between userland processes and
244 245 * device contracts. These interfaces record the acknowledgement
245 246 * or lack thereof for negotiation events and help determine if the
246 247 * negotiated event should occur.
247 248 *
248 249 * "backend routines"
249 250 * -----------------
250 251 * The backend routines form the interface between the I/O framework
251 252 * and the device contract subsystem. These routines, allow the I/O
252 253 * framework to call into the device contract subsystem to notify it of
253 254 * impending changes to a device state as well as to inform of the
254 255 * final disposition of such attempted state changes. Routines in this
255 256 * class include contract_device_offline() that indicates an attempt to
256 257 * offline a device, contract_device_degrade() that indicates that
257 258 * a device is moving to the degraded state and contract_device_negend()
258 259 * that is used by the I/O framework to inform the contracts subsystem of
259 260 * the final disposition of an attempted operation.
260 261 *
261 262 * SUMMARY
262 263 * -------
263 264 * A contract starts its life as a template. A process allocates a device
264 265 * contract template and sets various terms:
265 266 * The A-set
266 267 * The device minor node
267 268 * Critical and informative events
268 269 * The noneg i.e. no negotition term
269 270 * Setting of these terms in the template is done via the
270 271 * ctmpl_device_set() entry point in this file. A process can query a
271 272 * template to determine the terms already set in the template - this is
272 273 * facilitated by the ctmpl_device_get() routine.
273 274 *
274 275 * Once all the appropriate terms are set, the contract is instantiated via
275 276 * one of two methods
276 277 * - via an explicit create operation - this is facilitated by the
277 278 * ctmpl_device_create() entry point
278 279 * - synchronously with the open(2) system call - this is achieved via the
279 280 * contract_device_open() routine.
280 281 * The core work for both these above functions is done by
281 282 * contract_device_create()
282 283 *
283 284 * A contract once created can be queried for its status. Support for
284 285 * status info is provided by both the common contracts framework and by
285 286 * the "device" contract type. If the level of detail requested is
286 287 * CTD_COMMON, only the common contract framework data is used. Higher
287 288 * levels of detail result in calls to contract_device_status() to supply
288 289 * device contract type specific status information.
289 290 *
290 291 * A contract once created may be abandoned either explicitly or implictly.
291 292 * In either case, the contract_device_abandon() function is invoked. This
292 293 * function merely calls contract_destroy() which moves the contract to
293 294 * the DEAD state. The device contract portion of destroy processing is
294 295 * provided by contract_device_destroy() which merely disassociates the
295 296 * contract from its device devinfo node. A contract in the DEAD state is
296 297 * not freed. It hanbgs around until all references to the contract are
297 298 * gone. When that happens, the contract is finally deallocated. The
298 299 * device contract specific portion of the free is done by
299 300 * contract_device_free() which finally frees the device contract specific
300 301 * data structure (cont_device_t).
301 302 *
302 303 * When a device undergoes a state change, the I/O framework calls the
303 304 * corresponding device contract entry point. For example, when a device
304 305 * is about to go OFFLINE, the routine contract_device_offline() is
305 306 * invoked. Similarly if a device moves to DEGRADED state, the routine
306 307 * contract_device_degrade() function is called. These functions call the
307 308 * core routine contract_device_publish(). This function determines via
308 309 * the function is_sync_neg() whether an event is a synchronous (i.e.
309 310 * negotiable) event or not. In the former case contract_device_publish()
310 311 * publishes a CTE_NEG event and then waits in wait_for_acks() for ACKs
311 312 * and/or NACKs from contract holders. In the latter case, it simply
312 313 * publishes the event and does not wait. In the negotiation case, ACKs or
313 314 * NACKs from userland consumers results in contract_device_ack_nack()
314 315 * being called where the result of the negotiation is recorded in the
315 316 * contract data structure. Once all outstanding contract owners have
316 317 * responded, the device contract code in wait_for_acks() determines the
317 318 * final result of the negotiation. A single NACK overrides all other ACKs
318 319 * If there is no NACK, then a single ACK will result in an overall ACK
319 320 * result. If there are no ACKs or NACKs, then the result CT_NONE is
320 321 * returned back to the I/O framework. Once the event is permitted or
321 322 * blocked, the I/O framework proceeds or aborts the state change. The
322 323 * I/O framework then calls contract_device_negend() with a result code
323 324 * indicating final disposition of the event. This call releases the
324 325 * barrier and other state associated with the previous negotiation,
325 326 * which permits the next event (if any) to come into the device contract
326 327 * framework.
327 328 *
328 329 * Finally, a device that has outstanding contracts may be removed from
329 330 * the system which results in its devinfo node being freed. The devinfo
330 331 * free routine in the I/O framework, calls into the device contract
331 332 * function - contract_device_remove_dip(). This routine, disassociates
332 333 * the dip from all contracts associated with the contract being freed,
333 334 * allowing the devinfo node to be freed.
334 335 *
335 336 * LOCKING
336 337 * ---------
337 338 * There are four sets of data that need to be protected by locks
338 339 *
339 340 * i) device contract specific portion of the contract template - This data
340 341 * is protected by the template lock ctmpl_lock.
341 342 *
342 343 * ii) device contract specific portion of the contract - This data is
343 344 * protected by the contract lock ct_lock
344 345 *
345 346 * iii) The linked list of contracts hanging off a devinfo node - This
346 347 * list is protected by the per-devinfo node lock devi_ct_lock
347 348 *
348 349 * iv) Finally there is a barrier, controlled by devi_ct_lock, devi_ct_cv
349 350 * and devi_ct_count that controls state changes to a dip
350 351 *
351 352 * The template lock is independent in that none of the other locks in this
352 353 * file may be taken while holding the template lock (and vice versa).
353 354 *
354 355 * The remaining three locks have the following lock order
355 356 *
356 357 * devi_ct_lock -> ct_count barrier -> ct_lock
357 358 *
358 359 */
359 360
360 361 static cont_device_t *contract_device_create(ctmpl_device_t *dtmpl, dev_t dev,
361 362 int spec_type, proc_t *owner, int *errorp);
362 363
363 364 /* barrier routines */
364 365 static void ct_barrier_acquire(dev_info_t *dip);
365 366 static void ct_barrier_release(dev_info_t *dip);
366 367 static int ct_barrier_held(dev_info_t *dip);
367 368 static int ct_barrier_empty(dev_info_t *dip);
368 369 static void ct_barrier_wait_for_release(dev_info_t *dip);
369 370 static int ct_barrier_wait_for_empty(dev_info_t *dip, int secs);
370 371 static void ct_barrier_decr(dev_info_t *dip);
371 372 static void ct_barrier_incr(dev_info_t *dip);
372 373
373 374 ct_type_t *device_type;
374 375
375 376 /*
376 377 * Macro predicates for determining when events should be sent and how.
377 378 */
378 379 #define EVSENDP(ctd, flag) \
379 380 ((ctd->cond_contract.ct_ev_info | ctd->cond_contract.ct_ev_crit) & flag)
380 381
381 382 #define EVINFOP(ctd, flag) \
382 383 ((ctd->cond_contract.ct_ev_crit & flag) == 0)
383 384
384 385 /*
385 386 * State transition table showing which transitions are synchronous and which
386 387 * are not.
387 388 */
388 389 struct ct_dev_negtable {
389 390 uint_t st_old;
390 391 uint_t st_new;
391 392 uint_t st_neg;
392 393 } ct_dev_negtable[] = {
393 394 {CT_DEV_EV_ONLINE, CT_DEV_EV_OFFLINE, 1},
394 395 {CT_DEV_EV_ONLINE, CT_DEV_EV_DEGRADED, 0},
395 396 {CT_DEV_EV_DEGRADED, CT_DEV_EV_ONLINE, 0},
396 397 {CT_DEV_EV_DEGRADED, CT_DEV_EV_OFFLINE, 1},
397 398 {0}
398 399 };
399 400
400 401 /*
401 402 * Device contract template implementation
402 403 */
403 404
404 405 /*
405 406 * ctmpl_device_dup
406 407 *
407 408 * The device contract template dup entry point.
408 409 * This simply copies all the fields (generic as well as device contract
409 410 * specific) fields of the original.
410 411 */
411 412 static struct ct_template *
412 413 ctmpl_device_dup(struct ct_template *template)
413 414 {
414 415 ctmpl_device_t *new;
415 416 ctmpl_device_t *old = template->ctmpl_data;
416 417 char *buf;
417 418 char *minor;
418 419
419 420 new = kmem_zalloc(sizeof (ctmpl_device_t), KM_SLEEP);
420 421 buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
421 422
422 423 /*
423 424 * copy generic fields.
424 425 * ctmpl_copy returns with old template lock held
425 426 */
426 427 ctmpl_copy(&new->ctd_ctmpl, template);
427 428
428 429 new->ctd_ctmpl.ctmpl_data = new;
429 430 new->ctd_aset = old->ctd_aset;
430 431 new->ctd_minor = NULL;
431 432 new->ctd_noneg = old->ctd_noneg;
432 433
433 434 if (old->ctd_minor) {
434 435 ASSERT(strlen(old->ctd_minor) + 1 <= MAXPATHLEN);
435 436 bcopy(old->ctd_minor, buf, strlen(old->ctd_minor) + 1);
436 437 } else {
437 438 kmem_free(buf, MAXPATHLEN);
438 439 buf = NULL;
439 440 }
440 441
441 442 mutex_exit(&template->ctmpl_lock);
442 443 if (buf) {
443 444 minor = i_ddi_strdup(buf, KM_SLEEP);
444 445 kmem_free(buf, MAXPATHLEN);
445 446 buf = NULL;
446 447 } else {
447 448 minor = NULL;
448 449 }
449 450 mutex_enter(&template->ctmpl_lock);
450 451
451 452 if (minor) {
452 453 new->ctd_minor = minor;
453 454 }
454 455
455 456 ASSERT(buf == NULL);
456 457 return (&new->ctd_ctmpl);
457 458 }
458 459
459 460 /*
460 461 * ctmpl_device_free
461 462 *
462 463 * The device contract template free entry point. Just
463 464 * frees the template.
464 465 */
465 466 static void
466 467 ctmpl_device_free(struct ct_template *template)
467 468 {
468 469 ctmpl_device_t *dtmpl = template->ctmpl_data;
469 470
470 471 if (dtmpl->ctd_minor)
471 472 kmem_free(dtmpl->ctd_minor, strlen(dtmpl->ctd_minor) + 1);
472 473
473 474 kmem_free(dtmpl, sizeof (ctmpl_device_t));
474 475 }
475 476
476 477 /*
477 478 * SAFE_EV is the set of events which a non-privileged process is
478 479 * allowed to make critical. An unprivileged device contract owner has
479 480 * no control over when a device changes state, so all device events
480 481 * can be in the critical set.
481 482 *
482 483 * EXCESS tells us if "value", a critical event set, requires
483 484 * additional privilege. For device contracts EXCESS currently
484 485 * evaluates to 0.
485 486 */
486 487 #define SAFE_EV (CT_DEV_ALLEVENT)
487 488 #define EXCESS(value) ((value) & ~SAFE_EV)
488 489
489 490
490 491 /*
491 492 * ctmpl_device_set
492 493 *
493 494 * The device contract template set entry point. Sets various terms in the
494 495 * template. The non-negotiable term can only be set if the process has
495 496 * the {PRIV_SYS_DEVICES} privilege asserted in its effective set.
496 497 */
497 498 static int
498 499 ctmpl_device_set(struct ct_template *tmpl, ct_kparam_t *kparam,
499 500 const cred_t *cr)
500 501 {
501 502 ctmpl_device_t *dtmpl = tmpl->ctmpl_data;
502 503 ct_param_t *param = &kparam->param;
503 504 int error;
504 505 dev_info_t *dip;
505 506 int spec_type;
506 507 uint64_t param_value;
507 508 char *str_value;
508 509
509 510 ASSERT(MUTEX_HELD(&tmpl->ctmpl_lock));
510 511
511 512 if (param->ctpm_id == CTDP_MINOR) {
512 513 str_value = (char *)kparam->ctpm_kbuf;
513 514 str_value[param->ctpm_size - 1] = '\0';
514 515 } else {
515 516 if (param->ctpm_size < sizeof (uint64_t))
516 517 return (EINVAL);
517 518 param_value = *(uint64_t *)kparam->ctpm_kbuf;
518 519 }
519 520
520 521 switch (param->ctpm_id) {
521 522 case CTDP_ACCEPT:
522 523 if (param_value & ~CT_DEV_ALLEVENT)
523 524 return (EINVAL);
524 525 if (param_value == 0)
525 526 return (EINVAL);
526 527 if (param_value == CT_DEV_ALLEVENT)
527 528 return (EINVAL);
528 529
529 530 dtmpl->ctd_aset = param_value;
530 531 break;
531 532 case CTDP_NONEG:
532 533 if (param_value != CTDP_NONEG_SET &&
533 534 param_value != CTDP_NONEG_CLEAR)
534 535 return (EINVAL);
535 536
536 537 /*
537 538 * only privileged processes can designate a contract
538 539 * non-negotiatble.
539 540 */
540 541 if (param_value == CTDP_NONEG_SET &&
541 542 (error = secpolicy_sys_devices(cr)) != 0) {
542 543 return (error);
543 544 }
544 545
545 546 dtmpl->ctd_noneg = param_value;
546 547 break;
547 548
548 549 case CTDP_MINOR:
549 550 if (*str_value != '/' ||
550 551 strncmp(str_value, "/devices/",
551 552 strlen("/devices/")) == 0 ||
552 553 strstr(str_value, "../devices/") != NULL ||
553 554 strchr(str_value, ':') == NULL) {
554 555 return (EINVAL);
555 556 }
556 557
557 558 spec_type = 0;
558 559 dip = NULL;
559 560 if (resolve_pathname(str_value, &dip, NULL, &spec_type) != 0) {
560 561 return (ERANGE);
561 562 }
562 563 ddi_release_devi(dip);
563 564
564 565 if (spec_type != S_IFCHR && spec_type != S_IFBLK) {
565 566 return (EINVAL);
566 567 }
567 568
568 569 if (dtmpl->ctd_minor != NULL) {
569 570 kmem_free(dtmpl->ctd_minor,
570 571 strlen(dtmpl->ctd_minor) + 1);
571 572 }
572 573 dtmpl->ctd_minor = i_ddi_strdup(str_value, KM_SLEEP);
573 574 break;
574 575 case CTP_EV_CRITICAL:
575 576 /*
576 577 * Currently for device contracts, any event
577 578 * may be added to the critical set. We retain the
578 579 * following code however for future enhancements.
579 580 */
580 581 if (EXCESS(param_value) &&
581 582 (error = secpolicy_contract_event(cr)) != 0)
582 583 return (error);
583 584 tmpl->ctmpl_ev_crit = param_value;
584 585 break;
585 586 default:
586 587 return (EINVAL);
587 588 }
588 589
589 590 return (0);
590 591 }
591 592
592 593 /*
593 594 * ctmpl_device_get
594 595 *
595 596 * The device contract template get entry point. Simply fetches and
596 597 * returns the value of the requested term.
597 598 */
598 599 static int
599 600 ctmpl_device_get(struct ct_template *template, ct_kparam_t *kparam)
600 601 {
601 602 ctmpl_device_t *dtmpl = template->ctmpl_data;
602 603 ct_param_t *param = &kparam->param;
603 604 uint64_t *param_value = kparam->ctpm_kbuf;
604 605
605 606 ASSERT(MUTEX_HELD(&template->ctmpl_lock));
606 607
607 608 if (param->ctpm_id == CTDP_ACCEPT ||
608 609 param->ctpm_id == CTDP_NONEG) {
609 610 if (param->ctpm_size < sizeof (uint64_t))
610 611 return (EINVAL);
611 612 kparam->ret_size = sizeof (uint64_t);
612 613 }
613 614
614 615 switch (param->ctpm_id) {
615 616 case CTDP_ACCEPT:
616 617 *param_value = dtmpl->ctd_aset;
617 618 break;
618 619 case CTDP_NONEG:
619 620 *param_value = dtmpl->ctd_noneg;
620 621 break;
621 622 case CTDP_MINOR:
622 623 if (dtmpl->ctd_minor) {
623 624 kparam->ret_size = strlcpy((char *)kparam->ctpm_kbuf,
624 625 dtmpl->ctd_minor, param->ctpm_size);
625 626 kparam->ret_size++;
626 627 } else {
627 628 return (ENOENT);
628 629 }
629 630 break;
630 631 default:
631 632 return (EINVAL);
632 633 }
633 634
634 635 return (0);
635 636 }
636 637
637 638 /*
638 639 * Device contract type specific portion of creating a contract using
639 640 * a specified template
640 641 */
641 642 /*ARGSUSED*/
642 643 int
643 644 ctmpl_device_create(ct_template_t *template, ctid_t *ctidp)
644 645 {
645 646 ctmpl_device_t *dtmpl;
646 647 char *buf;
647 648 dev_t dev;
648 649 int spec_type;
649 650 int error;
650 651 cont_device_t *ctd;
651 652
652 653 if (ctidp == NULL)
653 654 return (EINVAL);
654 655
655 656 buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
656 657
657 658 dtmpl = template->ctmpl_data;
658 659
659 660 mutex_enter(&template->ctmpl_lock);
660 661 if (dtmpl->ctd_minor == NULL) {
661 662 /* incomplete template */
662 663 mutex_exit(&template->ctmpl_lock);
663 664 kmem_free(buf, MAXPATHLEN);
664 665 return (EINVAL);
665 666 } else {
666 667 ASSERT(strlen(dtmpl->ctd_minor) < MAXPATHLEN);
667 668 bcopy(dtmpl->ctd_minor, buf, strlen(dtmpl->ctd_minor) + 1);
668 669 }
669 670 mutex_exit(&template->ctmpl_lock);
670 671
671 672 spec_type = 0;
672 673 dev = NODEV;
673 674 if (resolve_pathname(buf, NULL, &dev, &spec_type) != 0 ||
674 675 dev == NODEV || dev == DDI_DEV_T_ANY || dev == DDI_DEV_T_NONE ||
675 676 (spec_type != S_IFCHR && spec_type != S_IFBLK)) {
676 677 CT_DEBUG((CE_WARN,
677 678 "tmpl_create: failed to find device: %s", buf));
678 679 kmem_free(buf, MAXPATHLEN);
679 680 return (ERANGE);
680 681 }
681 682 kmem_free(buf, MAXPATHLEN);
682 683
683 684 ctd = contract_device_create(template->ctmpl_data,
684 685 dev, spec_type, curproc, &error);
685 686
686 687 if (ctd == NULL) {
687 688 CT_DEBUG((CE_WARN, "Failed to create device contract for "
688 689 "process (%d) with device (devt = %lu, spec_type = %s)",
689 690 curproc->p_pid, dev,
690 691 spec_type == S_IFCHR ? "S_IFCHR" : "S_IFBLK"));
691 692 return (error);
692 693 }
693 694
694 695 mutex_enter(&ctd->cond_contract.ct_lock);
695 696 *ctidp = ctd->cond_contract.ct_id;
696 697 mutex_exit(&ctd->cond_contract.ct_lock);
697 698
698 699 return (0);
699 700 }
700 701
701 702 /*
702 703 * Device contract specific template entry points
703 704 */
704 705 static ctmplops_t ctmpl_device_ops = {
705 706 ctmpl_device_dup, /* ctop_dup */
706 707 ctmpl_device_free, /* ctop_free */
707 708 ctmpl_device_set, /* ctop_set */
708 709 ctmpl_device_get, /* ctop_get */
709 710 ctmpl_device_create, /* ctop_create */
710 711 CT_DEV_ALLEVENT /* all device events bitmask */
711 712 };
712 713
713 714
714 715 /*
715 716 * Device contract implementation
716 717 */
717 718
718 719 /*
719 720 * contract_device_default
720 721 *
721 722 * The device contract default template entry point. Creates a
722 723 * device contract template with a default A-set and no "noneg" ,
723 724 * with informative degrade events and critical offline events.
724 725 * There is no default minor path.
725 726 */
726 727 static ct_template_t *
727 728 contract_device_default(void)
728 729 {
729 730 ctmpl_device_t *new;
730 731
731 732 new = kmem_zalloc(sizeof (ctmpl_device_t), KM_SLEEP);
732 733 ctmpl_init(&new->ctd_ctmpl, &ctmpl_device_ops, device_type, new);
733 734
734 735 new->ctd_aset = CT_DEV_EV_ONLINE | CT_DEV_EV_DEGRADED;
735 736 new->ctd_noneg = 0;
736 737 new->ctd_ctmpl.ctmpl_ev_info = CT_DEV_EV_DEGRADED;
737 738 new->ctd_ctmpl.ctmpl_ev_crit = CT_DEV_EV_OFFLINE;
738 739
739 740 return (&new->ctd_ctmpl);
740 741 }
741 742
742 743 /*
743 744 * contract_device_free
744 745 *
745 746 * Destroys the device contract specific portion of a contract and
746 747 * frees the contract.
747 748 */
748 749 static void
749 750 contract_device_free(contract_t *ct)
750 751 {
751 752 cont_device_t *ctd = ct->ct_data;
752 753
753 754 ASSERT(ctd->cond_minor);
754 755 ASSERT(strlen(ctd->cond_minor) < MAXPATHLEN);
755 756 kmem_free(ctd->cond_minor, strlen(ctd->cond_minor) + 1);
756 757
757 758 ASSERT(ctd->cond_devt != DDI_DEV_T_ANY &&
758 759 ctd->cond_devt != DDI_DEV_T_NONE && ctd->cond_devt != NODEV);
759 760
760 761 ASSERT(ctd->cond_spec == S_IFBLK || ctd->cond_spec == S_IFCHR);
761 762
762 763 ASSERT(!(ctd->cond_aset & ~CT_DEV_ALLEVENT));
763 764 ASSERT(ctd->cond_noneg == 0 || ctd->cond_noneg == 1);
764 765
765 766 ASSERT(!(ctd->cond_currev_type & ~CT_DEV_ALLEVENT));
766 767 ASSERT(!(ctd->cond_currev_ack & ~(CT_ACK | CT_NACK)));
767 768
768 769 ASSERT((ctd->cond_currev_id > 0) ^ (ctd->cond_currev_type == 0));
769 770 ASSERT((ctd->cond_currev_id > 0) || (ctd->cond_currev_ack == 0));
770 771
771 772 ASSERT(!list_link_active(&ctd->cond_next));
772 773
773 774 kmem_free(ctd, sizeof (cont_device_t));
774 775 }
775 776
776 777 /*
777 778 * contract_device_abandon
778 779 *
779 780 * The device contract abandon entry point.
780 781 */
781 782 static void
782 783 contract_device_abandon(contract_t *ct)
783 784 {
784 785 ASSERT(MUTEX_HELD(&ct->ct_lock));
785 786
786 787 /*
787 788 * device contracts cannot be inherited or orphaned.
788 789 * Move the contract to the DEAD_STATE. It will be freed
789 790 * once all references to it are gone.
790 791 */
791 792 contract_destroy(ct);
792 793 }
793 794
794 795 /*
795 796 * contract_device_destroy
796 797 *
797 798 * The device contract destroy entry point.
798 799 * Called from contract_destroy() to do any type specific destroy. Note
799 800 * that destroy is a misnomer - this does not free the contract, it only
800 801 * moves it to the dead state. A contract is actually freed via
801 802 * contract_rele() -> contract_dtor(), contop_free()
802 803 */
803 804 static void
804 805 contract_device_destroy(contract_t *ct)
805 806 {
806 807 cont_device_t *ctd;
807 808 dev_info_t *dip;
808 809
809 810 ASSERT(MUTEX_HELD(&ct->ct_lock));
810 811
811 812 for (;;) {
812 813 ctd = ct->ct_data;
813 814 dip = ctd->cond_dip;
814 815 if (dip == NULL) {
815 816 /*
816 817 * The dip has been removed, this is a dangling contract
817 818 * Check that dip linkages are NULL
818 819 */
819 820 ASSERT(!list_link_active(&ctd->cond_next));
820 821 CT_DEBUG((CE_NOTE, "contract_device_destroy:"
821 822 " contract has no devinfo node. contract ctid : %d",
822 823 ct->ct_id));
823 824 return;
824 825 }
825 826
826 827 /*
827 828 * The intended lock order is : devi_ct_lock -> ct_count
828 829 * barrier -> ct_lock.
829 830 * However we can't do this here as dropping the ct_lock allows
830 831 * a race condition with i_ddi_free_node()/
831 832 * contract_device_remove_dip() which may free off dip before
832 833 * we can take devi_ct_lock. So use mutex_tryenter to avoid
833 834 * dropping ct_lock until we have acquired devi_ct_lock.
834 835 */
835 836 if (mutex_tryenter(&(DEVI(dip)->devi_ct_lock)) != 0)
836 837 break;
837 838 mutex_exit(&ct->ct_lock);
838 839 delay(drv_usectohz(1000));
839 840 mutex_enter(&ct->ct_lock);
840 841 }
841 842 mutex_exit(&ct->ct_lock);
842 843
843 844 /*
844 845 * Waiting for the barrier to be released is strictly speaking not
845 846 * necessary. But it simplifies the implementation of
846 847 * contract_device_publish() by establishing the invariant that
847 848 * device contracts cannot go away during negotiation.
848 849 */
849 850 ct_barrier_wait_for_release(dip);
850 851 mutex_enter(&ct->ct_lock);
851 852
852 853 list_remove(&(DEVI(dip)->devi_ct), ctd);
853 854 ctd->cond_dip = NULL; /* no longer linked to dip */
854 855 contract_rele(ct); /* remove hold for dip linkage */
855 856
856 857 mutex_exit(&ct->ct_lock);
857 858 mutex_exit(&(DEVI(dip)->devi_ct_lock));
858 859 mutex_enter(&ct->ct_lock);
859 860 }
860 861
861 862 /*
862 863 * contract_device_status
863 864 *
864 865 * The device contract status entry point. Called when level of "detail"
865 866 * is either CTD_FIXED or CTD_ALL
866 867 *
867 868 */
868 869 static void
869 870 contract_device_status(contract_t *ct, zone_t *zone, int detail, nvlist_t *nvl,
870 871 void *status, model_t model)
871 872 {
872 873 cont_device_t *ctd = ct->ct_data;
873 874
874 875 ASSERT(detail == CTD_FIXED || detail == CTD_ALL);
875 876
876 877 mutex_enter(&ct->ct_lock);
877 878 contract_status_common(ct, zone, status, model);
878 879
879 880 /*
880 881 * There's no need to hold the contract lock while accessing static
881 882 * data like aset or noneg. But since we need the lock to access other
882 883 * data like state, we hold it anyway.
883 884 */
884 885 VERIFY(nvlist_add_uint32(nvl, CTDS_STATE, ctd->cond_state) == 0);
885 886 VERIFY(nvlist_add_uint32(nvl, CTDS_ASET, ctd->cond_aset) == 0);
886 887 VERIFY(nvlist_add_uint32(nvl, CTDS_NONEG, ctd->cond_noneg) == 0);
887 888
888 889 if (detail == CTD_FIXED) {
889 890 mutex_exit(&ct->ct_lock);
890 891 return;
891 892 }
892 893
893 894 ASSERT(ctd->cond_minor);
894 895 VERIFY(nvlist_add_string(nvl, CTDS_MINOR, ctd->cond_minor) == 0);
895 896
896 897 mutex_exit(&ct->ct_lock);
897 898 }
898 899
899 900 /*
900 901 * Converts a result integer into the corresponding string. Used for printing
901 902 * messages
902 903 */
903 904 static char *
904 905 result_str(uint_t result)
905 906 {
906 907 switch (result) {
907 908 case CT_ACK:
908 909 return ("CT_ACK");
909 910 case CT_NACK:
910 911 return ("CT_NACK");
911 912 case CT_NONE:
912 913 return ("CT_NONE");
913 914 default:
914 915 return ("UNKNOWN");
915 916 }
916 917 }
917 918
918 919 /*
919 920 * Converts a device state integer constant into the corresponding string.
920 921 * Used to print messages.
921 922 */
922 923 static char *
923 924 state_str(uint_t state)
924 925 {
925 926 switch (state) {
926 927 case CT_DEV_EV_ONLINE:
927 928 return ("ONLINE");
928 929 case CT_DEV_EV_DEGRADED:
929 930 return ("DEGRADED");
930 931 case CT_DEV_EV_OFFLINE:
931 932 return ("OFFLINE");
932 933 default:
933 934 return ("UNKNOWN");
934 935 }
935 936 }
936 937
937 938 /*
938 939 * Routine that determines if a particular CT_DEV_EV_? event corresponds to a
939 940 * synchronous state change or not.
940 941 */
941 942 static int
942 943 is_sync_neg(uint_t old, uint_t new)
943 944 {
944 945 int i;
945 946
946 947 ASSERT(old & CT_DEV_ALLEVENT);
947 948 ASSERT(new & CT_DEV_ALLEVENT);
948 949
949 950 if (old == new) {
950 951 CT_DEBUG((CE_WARN, "is_sync_neg: transition to same state: %s",
951 952 state_str(new)));
952 953 return (-2);
953 954 }
954 955
955 956 for (i = 0; ct_dev_negtable[i].st_new != 0; i++) {
956 957 if (old == ct_dev_negtable[i].st_old &&
957 958 new == ct_dev_negtable[i].st_new) {
958 959 return (ct_dev_negtable[i].st_neg);
959 960 }
960 961 }
961 962
962 963 CT_DEBUG((CE_WARN, "is_sync_neg: Unsupported state transition: "
963 964 "old = %s -> new = %s", state_str(old), state_str(new)));
964 965
965 966 return (-1);
966 967 }
967 968
968 969 /*
969 970 * Used to cleanup cached dv_nodes so that when a device is released by
970 971 * a contract holder, its devinfo node can be successfully detached.
971 972 */
972 973 static int
973 974 contract_device_dvclean(dev_info_t *dip)
974 975 {
975 976 char *devnm;
976 977 dev_info_t *pdip;
977 978
978 979 ASSERT(dip);
979 980
980 981 /* pdip can be NULL if we have contracts against the root dip */
981 982 pdip = ddi_get_parent(dip);
982 983
983 984 if (pdip && DEVI_BUSY_OWNED(pdip) || !pdip && DEVI_BUSY_OWNED(dip)) {
984 985 char *path;
985 986
986 987 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
987 988 (void) ddi_pathname(dip, path);
988 989 CT_DEBUG((CE_WARN, "ct_dv_clean: Parent node is busy owned, "
989 990 "device=%s", path));
990 991 kmem_free(path, MAXPATHLEN);
991 992 return (EDEADLOCK);
992 993 }
993 994
994 995 if (pdip) {
995 996 devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
996 997 (void) ddi_deviname(dip, devnm);
997 998 (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
998 999 kmem_free(devnm, MAXNAMELEN + 1);
999 1000 } else {
1000 1001 (void) devfs_clean(dip, NULL, DV_CLEAN_FORCE);
1001 1002 }
1002 1003
1003 1004 return (0);
1004 1005 }
1005 1006
1006 1007 /*
1007 1008 * Endpoint of a ct_ctl_ack() or ct_ctl_nack() call from userland.
1008 1009 * Results in the ACK or NACK being recorded on the dip for one particular
1009 1010 * contract. The device contracts framework evaluates the ACK/NACKs for all
1010 1011 * contracts against a device to determine if a particular device state change
1011 1012 * should be allowed.
1012 1013 */
1013 1014 static int
1014 1015 contract_device_ack_nack(contract_t *ct, uint_t evtype, uint64_t evid,
1015 1016 uint_t cmd)
1016 1017 {
1017 1018 cont_device_t *ctd = ct->ct_data;
1018 1019 dev_info_t *dip;
1019 1020 ctid_t ctid;
1020 1021 int error;
1021 1022
1022 1023 ctid = ct->ct_id;
1023 1024
1024 1025 CT_DEBUG((CE_NOTE, "ack_nack: entered: ctid %d", ctid));
1025 1026
1026 1027 mutex_enter(&ct->ct_lock);
1027 1028 CT_DEBUG((CE_NOTE, "ack_nack: contract lock acquired: %d", ctid));
1028 1029
1029 1030 dip = ctd->cond_dip;
1030 1031
1031 1032 ASSERT(ctd->cond_minor);
1032 1033 ASSERT(strlen(ctd->cond_minor) < MAXPATHLEN);
1033 1034
1034 1035 /*
1035 1036 * Negotiation only if new state is not in A-set
1036 1037 */
1037 1038 ASSERT(!(ctd->cond_aset & evtype));
1038 1039
1039 1040 /*
1040 1041 * Negotiation only if transition is synchronous
1041 1042 */
1042 1043 ASSERT(is_sync_neg(ctd->cond_state, evtype));
1043 1044
1044 1045 /*
1045 1046 * We shouldn't be negotiating if the "noneg" flag is set
1046 1047 */
1047 1048 ASSERT(!ctd->cond_noneg);
1048 1049
1049 1050 if (dip)
1050 1051 ndi_hold_devi(dip);
1051 1052
1052 1053 mutex_exit(&ct->ct_lock);
1053 1054
1054 1055 /*
1055 1056 * dv_clean only if !NACK and offline state change
1056 1057 */
1057 1058 if (cmd != CT_NACK && evtype == CT_DEV_EV_OFFLINE && dip) {
1058 1059 CT_DEBUG((CE_NOTE, "ack_nack: dv_clean: %d", ctid));
1059 1060 error = contract_device_dvclean(dip);
1060 1061 if (error != 0) {
1061 1062 CT_DEBUG((CE_NOTE, "ack_nack: dv_clean: failed: %d",
1062 1063 ctid));
1063 1064 ddi_release_devi(dip);
1064 1065 }
1065 1066 }
1066 1067
1067 1068 mutex_enter(&ct->ct_lock);
1068 1069
1069 1070 if (dip)
1070 1071 ddi_release_devi(dip);
1071 1072
1072 1073 if (dip == NULL) {
1073 1074 if (ctd->cond_currev_id != evid) {
1074 1075 CT_DEBUG((CE_WARN, "%sACK for non-current event "
1075 1076 "(type=%s, id=%llu) on removed device",
1076 1077 cmd == CT_NACK ? "N" : "",
1077 1078 state_str(evtype), (unsigned long long)evid));
1078 1079 CT_DEBUG((CE_NOTE, "ack_nack: error: ESRCH, ctid: %d",
1079 1080 ctid));
1080 1081 } else {
1081 1082 ASSERT(ctd->cond_currev_type == evtype);
1082 1083 CT_DEBUG((CE_WARN, "contract_ack: no such device: "
1083 1084 "ctid: %d", ctid));
1084 1085 }
1085 1086 error = (ct->ct_state == CTS_DEAD) ? ESRCH :
1086 1087 ((cmd == CT_NACK) ? ETIMEDOUT : 0);
1087 1088 mutex_exit(&ct->ct_lock);
1088 1089 return (error);
1089 1090 }
1090 1091
1091 1092 /*
1092 1093 * Must follow lock order: devi_ct_lock -> ct_count barrier - >ct_lock
1093 1094 */
1094 1095 mutex_exit(&ct->ct_lock);
1095 1096
1096 1097 mutex_enter(&DEVI(dip)->devi_ct_lock);
1097 1098 mutex_enter(&ct->ct_lock);
1098 1099 if (ctd->cond_currev_id != evid) {
1099 1100 char *buf;
1100 1101 mutex_exit(&ct->ct_lock);
1101 1102 mutex_exit(&DEVI(dip)->devi_ct_lock);
1102 1103 ndi_hold_devi(dip);
1103 1104 buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1104 1105 (void) ddi_pathname(dip, buf);
1105 1106 ddi_release_devi(dip);
1106 1107 CT_DEBUG((CE_WARN, "%sACK for non-current event"
1107 1108 "(type=%s, id=%llu) on device %s",
1108 1109 cmd == CT_NACK ? "N" : "",
1109 1110 state_str(evtype), (unsigned long long)evid, buf));
1110 1111 kmem_free(buf, MAXPATHLEN);
1111 1112 CT_DEBUG((CE_NOTE, "ack_nack: error: %d, ctid: %d",
1112 1113 cmd == CT_NACK ? ETIMEDOUT : 0, ctid));
1113 1114 return (cmd == CT_ACK ? 0 : ETIMEDOUT);
1114 1115 }
1115 1116
1116 1117 ASSERT(ctd->cond_currev_type == evtype);
1117 1118 ASSERT(cmd == CT_ACK || cmd == CT_NACK);
1118 1119
1119 1120 CT_DEBUG((CE_NOTE, "ack_nack: setting %sACK for ctid: %d",
1120 1121 cmd == CT_NACK ? "N" : "", ctid));
1121 1122
1122 1123 ctd->cond_currev_ack = cmd;
1123 1124 mutex_exit(&ct->ct_lock);
1124 1125
1125 1126 ct_barrier_decr(dip);
1126 1127 mutex_exit(&DEVI(dip)->devi_ct_lock);
1127 1128
1128 1129 CT_DEBUG((CE_NOTE, "ack_nack: normal exit: ctid: %d", ctid));
1129 1130
1130 1131 return (0);
1131 1132 }
1132 1133
1133 1134 /*
1134 1135 * Invoked when a userland contract holder approves (i.e. ACKs) a state change
1135 1136 */
1136 1137 static int
1137 1138 contract_device_ack(contract_t *ct, uint_t evtype, uint64_t evid)
1138 1139 {
1139 1140 return (contract_device_ack_nack(ct, evtype, evid, CT_ACK));
1140 1141 }
1141 1142
1142 1143 /*
1143 1144 * Invoked when a userland contract holder blocks (i.e. NACKs) a state change
1144 1145 */
1145 1146 static int
1146 1147 contract_device_nack(contract_t *ct, uint_t evtype, uint64_t evid)
1147 1148 {
1148 1149 return (contract_device_ack_nack(ct, evtype, evid, CT_NACK));
1149 1150 }
1150 1151
1151 1152 /*
1152 1153 * Creates a new contract synchronously with the breaking of an existing
1153 1154 * contract. Currently not supported.
1154 1155 */
1155 1156 /*ARGSUSED*/
1156 1157 static int
1157 1158 contract_device_newct(contract_t *ct)
1158 1159 {
1159 1160 return (ENOTSUP);
1160 1161 }
1161 1162
1162 1163 /*
1163 1164 * Core device contract implementation entry points
1164 1165 */
1165 1166 static contops_t contract_device_ops = {
1166 1167 contract_device_free, /* contop_free */
1167 1168 contract_device_abandon, /* contop_abandon */
1168 1169 contract_device_destroy, /* contop_destroy */
1169 1170 contract_device_status, /* contop_status */
1170 1171 contract_device_ack, /* contop_ack */
1171 1172 contract_device_nack, /* contop_nack */
1172 1173 contract_qack_notsup, /* contop_qack */
1173 1174 contract_device_newct /* contop_newct */
1174 1175 };
1175 1176
1176 1177 /*
1177 1178 * contract_device_init
1178 1179 *
1179 1180 * Initializes the device contract type.
1180 1181 */
1181 1182 void
1182 1183 contract_device_init(void)
1183 1184 {
1184 1185 device_type = contract_type_init(CTT_DEVICE, "device",
1185 1186 &contract_device_ops, contract_device_default);
1186 1187 }
1187 1188
1188 1189 /*
1189 1190 * contract_device_create
1190 1191 *
1191 1192 * create a device contract given template "tmpl" and the "owner" process.
1192 1193 * May fail and return NULL if project.max-contracts would have been exceeded.
1193 1194 *
1194 1195 * Common device contract creation routine called for both open-time and
1195 1196 * non-open time device contract creation
1196 1197 */
1197 1198 static cont_device_t *
1198 1199 contract_device_create(ctmpl_device_t *dtmpl, dev_t dev, int spec_type,
1199 1200 proc_t *owner, int *errorp)
1200 1201 {
1201 1202 cont_device_t *ctd;
1202 1203 char *minor;
1203 1204 char *path;
1204 1205 dev_info_t *dip;
1205 1206
1206 1207 ASSERT(dtmpl != NULL);
1207 1208 ASSERT(dev != NODEV && dev != DDI_DEV_T_ANY && dev != DDI_DEV_T_NONE);
1208 1209 ASSERT(spec_type == S_IFCHR || spec_type == S_IFBLK);
1209 1210 ASSERT(errorp);
1210 1211
1211 1212 *errorp = 0;
1212 1213
1213 1214 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1214 1215
1215 1216 mutex_enter(&dtmpl->ctd_ctmpl.ctmpl_lock);
1216 1217 ASSERT(strlen(dtmpl->ctd_minor) < MAXPATHLEN);
1217 1218 bcopy(dtmpl->ctd_minor, path, strlen(dtmpl->ctd_minor) + 1);
1218 1219 mutex_exit(&dtmpl->ctd_ctmpl.ctmpl_lock);
1219 1220
1220 1221 dip = e_ddi_hold_devi_by_path(path, 0);
1221 1222 if (dip == NULL) {
1222 1223 cmn_err(CE_WARN, "contract_create: Cannot find devinfo node "
1223 1224 "for device path (%s)", path);
1224 1225 kmem_free(path, MAXPATHLEN);
1225 1226 *errorp = ERANGE;
1226 1227 return (NULL);
1227 1228 }
1228 1229
1229 1230 /*
1230 1231 * Lock out any parallel contract negotiations
1231 1232 */
1232 1233 mutex_enter(&(DEVI(dip)->devi_ct_lock));
1233 1234 ct_barrier_acquire(dip);
1234 1235 mutex_exit(&(DEVI(dip)->devi_ct_lock));
1235 1236
1236 1237 minor = i_ddi_strdup(path, KM_SLEEP);
1237 1238 kmem_free(path, MAXPATHLEN);
1238 1239
1239 1240 (void) contract_type_pbundle(device_type, owner);
1240 1241
1241 1242 ctd = kmem_zalloc(sizeof (cont_device_t), KM_SLEEP);
1242 1243
1243 1244 /*
1244 1245 * Only we hold a refernce to this contract. Safe to access
1245 1246 * the fields without a ct_lock
1246 1247 */
1247 1248 ctd->cond_minor = minor;
1248 1249 /*
1249 1250 * It is safe to set the dip pointer in the contract
1250 1251 * as the contract will always be destroyed before the dip
1251 1252 * is released
1252 1253 */
1253 1254 ctd->cond_dip = dip;
1254 1255 ctd->cond_devt = dev;
1255 1256 ctd->cond_spec = spec_type;
1256 1257
1257 1258 /*
1258 1259 * Since we are able to lookup the device, it is either
1259 1260 * online or degraded
1260 1261 */
1261 1262 ctd->cond_state = DEVI_IS_DEVICE_DEGRADED(dip) ?
1262 1263 CT_DEV_EV_DEGRADED : CT_DEV_EV_ONLINE;
1263 1264
1264 1265 mutex_enter(&dtmpl->ctd_ctmpl.ctmpl_lock);
1265 1266 ctd->cond_aset = dtmpl->ctd_aset;
1266 1267 ctd->cond_noneg = dtmpl->ctd_noneg;
1267 1268
1268 1269 /*
1269 1270 * contract_ctor() initailizes the common portion of a contract
1270 1271 * contract_dtor() destroys the common portion of a contract
1271 1272 */
1272 1273 if (contract_ctor(&ctd->cond_contract, device_type, &dtmpl->ctd_ctmpl,
1273 1274 ctd, 0, owner, B_TRUE)) {
1274 1275 mutex_exit(&dtmpl->ctd_ctmpl.ctmpl_lock);
1275 1276 /*
1276 1277 * contract_device_free() destroys the type specific
1277 1278 * portion of a contract and frees the contract.
1278 1279 * The "minor" path and "cred" is a part of the type specific
1279 1280 * portion of the contract and will be freed by
1280 1281 * contract_device_free()
1281 1282 */
1282 1283 contract_device_free(&ctd->cond_contract);
1283 1284
1284 1285 /* release barrier */
1285 1286 mutex_enter(&(DEVI(dip)->devi_ct_lock));
1286 1287 ct_barrier_release(dip);
1287 1288 mutex_exit(&(DEVI(dip)->devi_ct_lock));
1288 1289
1289 1290 ddi_release_devi(dip);
1290 1291 *errorp = EAGAIN;
1291 1292 return (NULL);
1292 1293 }
1293 1294 mutex_exit(&dtmpl->ctd_ctmpl.ctmpl_lock);
1294 1295
1295 1296 mutex_enter(&ctd->cond_contract.ct_lock);
1296 1297 ctd->cond_contract.ct_ntime.ctm_total = CT_DEV_ACKTIME;
1297 1298 ctd->cond_contract.ct_qtime.ctm_total = CT_DEV_ACKTIME;
1298 1299 ctd->cond_contract.ct_ntime.ctm_start = -1;
1299 1300 ctd->cond_contract.ct_qtime.ctm_start = -1;
1300 1301 mutex_exit(&ctd->cond_contract.ct_lock);
1301 1302
1302 1303 /*
1303 1304 * Insert device contract into list hanging off the dip
1304 1305 * Bump up the ref-count on the contract to reflect this
1305 1306 */
1306 1307 contract_hold(&ctd->cond_contract);
1307 1308 mutex_enter(&(DEVI(dip)->devi_ct_lock));
1308 1309 list_insert_tail(&(DEVI(dip)->devi_ct), ctd);
1309 1310
1310 1311 /* release barrier */
1311 1312 ct_barrier_release(dip);
1312 1313 mutex_exit(&(DEVI(dip)->devi_ct_lock));
1313 1314
1314 1315 ddi_release_devi(dip);
1315 1316
1316 1317 return (ctd);
1317 1318 }
1318 1319
1319 1320 /*
1320 1321 * Called when a device is successfully opened to create an open-time contract
1321 1322 * i.e. synchronously with a device open.
1322 1323 */
1323 1324 int
1324 1325 contract_device_open(dev_t dev, int spec_type, contract_t **ctpp)
1325 1326 {
1326 1327 ctmpl_device_t *dtmpl;
1327 1328 ct_template_t *tmpl;
1328 1329 cont_device_t *ctd;
1329 1330 char *path;
1330 1331 klwp_t *lwp;
1331 1332 int error;
1332 1333
1333 1334 if (ctpp)
1334 1335 *ctpp = NULL;
1335 1336
1336 1337 /*
1337 1338 * Check if we are in user-context i.e. if we have an lwp
1338 1339 */
1339 1340 lwp = ttolwp(curthread);
1340 1341 if (lwp == NULL) {
1341 1342 CT_DEBUG((CE_NOTE, "contract_open: Not user-context"));
1342 1343 return (0);
1343 1344 }
1344 1345
1345 1346 tmpl = ctmpl_dup(lwp->lwp_ct_active[device_type->ct_type_index]);
1346 1347 if (tmpl == NULL) {
1347 1348 return (0);
1348 1349 }
1349 1350 dtmpl = tmpl->ctmpl_data;
1350 1351
1351 1352 /*
1352 1353 * If the user set a minor path in the template before an open,
1353 1354 * ignore it. We use the minor path of the actual minor opened.
1354 1355 */
1355 1356 mutex_enter(&tmpl->ctmpl_lock);
1356 1357 if (dtmpl->ctd_minor != NULL) {
1357 1358 CT_DEBUG((CE_NOTE, "contract_device_open(): Process %d: "
1358 1359 "ignoring device minor path in active template: %s",
1359 1360 curproc->p_pid, dtmpl->ctd_minor));
1360 1361 /*
1361 1362 * This is a copy of the actual activated template.
1362 1363 * Safe to make changes such as freeing the minor
1363 1364 * path in the template.
1364 1365 */
1365 1366 kmem_free(dtmpl->ctd_minor, strlen(dtmpl->ctd_minor) + 1);
1366 1367 dtmpl->ctd_minor = NULL;
1367 1368 }
1368 1369 mutex_exit(&tmpl->ctmpl_lock);
1369 1370
1370 1371 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1371 1372
1372 1373 if (ddi_dev_pathname(dev, spec_type, path) != DDI_SUCCESS) {
1373 1374 CT_DEBUG((CE_NOTE, "contract_device_open(): Failed to derive "
1374 1375 "minor path from dev_t,spec {%lu, %d} for process (%d)",
1375 1376 dev, spec_type, curproc->p_pid));
1376 1377 ctmpl_free(tmpl);
1377 1378 kmem_free(path, MAXPATHLEN);
1378 1379 return (1);
1379 1380 }
1380 1381
1381 1382 mutex_enter(&tmpl->ctmpl_lock);
1382 1383 ASSERT(dtmpl->ctd_minor == NULL);
1383 1384 dtmpl->ctd_minor = path;
1384 1385 mutex_exit(&tmpl->ctmpl_lock);
1385 1386
1386 1387 ctd = contract_device_create(dtmpl, dev, spec_type, curproc, &error);
1387 1388
1388 1389 mutex_enter(&tmpl->ctmpl_lock);
1389 1390 ASSERT(dtmpl->ctd_minor);
1390 1391 dtmpl->ctd_minor = NULL;
1391 1392 mutex_exit(&tmpl->ctmpl_lock);
1392 1393 ctmpl_free(tmpl);
1393 1394 kmem_free(path, MAXPATHLEN);
1394 1395
1395 1396 if (ctd == NULL) {
1396 1397 cmn_err(CE_NOTE, "contract_device_open(): Failed to "
1397 1398 "create device contract for process (%d) holding "
1398 1399 "device (devt = %lu, spec_type = %d)",
1399 1400 curproc->p_pid, dev, spec_type);
1400 1401 return (1);
1401 1402 }
1402 1403
1403 1404 if (ctpp) {
1404 1405 mutex_enter(&ctd->cond_contract.ct_lock);
1405 1406 *ctpp = &ctd->cond_contract;
1406 1407 mutex_exit(&ctd->cond_contract.ct_lock);
1407 1408 }
1408 1409 return (0);
1409 1410 }
1410 1411
1411 1412 /*
1412 1413 * Called during contract negotiation by the device contract framework to wait
1413 1414 * for ACKs or NACKs from contract holders. If all responses are not received
1414 1415 * before a specified timeout, this routine times out.
1415 1416 */
1416 1417 static uint_t
1417 1418 wait_for_acks(dev_info_t *dip, dev_t dev, int spec_type, uint_t evtype)
1418 1419 {
1419 1420 cont_device_t *ctd;
1420 1421 int timed_out = 0;
1421 1422 int result = CT_NONE;
1422 1423 int ack;
1423 1424 char *f = "wait_for_acks";
1424 1425
1425 1426 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
1426 1427 ASSERT(dip);
1427 1428 ASSERT(evtype & CT_DEV_ALLEVENT);
1428 1429 ASSERT(dev != NODEV && dev != DDI_DEV_T_NONE);
1429 1430 ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
1430 1431 (spec_type == S_IFBLK || spec_type == S_IFCHR));
1431 1432
1432 1433 CT_DEBUG((CE_NOTE, "%s: entered: dip: %p", f, (void *)dip));
1433 1434
1434 1435 if (ct_barrier_wait_for_empty(dip, CT_DEV_ACKTIME) == -1) {
1435 1436 /*
1436 1437 * some contract owner(s) didn't respond in time
1437 1438 */
1438 1439 CT_DEBUG((CE_NOTE, "%s: timed out: %p", f, (void *)dip));
1439 1440 timed_out = 1;
1440 1441 }
1441 1442
1442 1443 ack = 0;
1443 1444 for (ctd = list_head(&(DEVI(dip)->devi_ct)); ctd != NULL;
1444 1445 ctd = list_next(&(DEVI(dip)->devi_ct), ctd)) {
1445 1446
1446 1447 mutex_enter(&ctd->cond_contract.ct_lock);
1447 1448
1448 1449 ASSERT(ctd->cond_dip == dip);
1449 1450
1450 1451 if (dev != DDI_DEV_T_ANY && dev != ctd->cond_devt) {
1451 1452 mutex_exit(&ctd->cond_contract.ct_lock);
1452 1453 continue;
1453 1454 }
1454 1455 if (dev != DDI_DEV_T_ANY && spec_type != ctd->cond_spec) {
1455 1456 mutex_exit(&ctd->cond_contract.ct_lock);
1456 1457 continue;
1457 1458 }
1458 1459
1459 1460 /* skip if non-negotiable contract */
1460 1461 if (ctd->cond_noneg) {
1461 1462 mutex_exit(&ctd->cond_contract.ct_lock);
1462 1463 continue;
1463 1464 }
1464 1465
1465 1466 ASSERT(ctd->cond_currev_type == evtype);
1466 1467 if (ctd->cond_currev_ack == CT_NACK) {
1467 1468 CT_DEBUG((CE_NOTE, "%s: found a NACK,result = NACK: %p",
1468 1469 f, (void *)dip));
1469 1470 mutex_exit(&ctd->cond_contract.ct_lock);
1470 1471 return (CT_NACK);
1471 1472 } else if (ctd->cond_currev_ack == CT_ACK) {
1472 1473 ack = 1;
1473 1474 CT_DEBUG((CE_NOTE, "%s: found a ACK: %p",
1474 1475 f, (void *)dip));
1475 1476 }
1476 1477 mutex_exit(&ctd->cond_contract.ct_lock);
1477 1478 }
1478 1479
1479 1480 if (ack) {
1480 1481 result = CT_ACK;
1481 1482 CT_DEBUG((CE_NOTE, "%s: result = ACK, dip=%p", f, (void *)dip));
1482 1483 } else if (timed_out) {
1483 1484 result = CT_NONE;
1484 1485 CT_DEBUG((CE_NOTE, "%s: result = NONE (timed-out), dip=%p",
1485 1486 f, (void *)dip));
1486 1487 } else {
1487 1488 CT_DEBUG((CE_NOTE, "%s: result = NONE, dip=%p",
1488 1489 f, (void *)dip));
1489 1490 }
1490 1491
1491 1492
1492 1493 return (result);
1493 1494 }
1494 1495
1495 1496 /*
1496 1497 * Determines the current state of a device (i.e a devinfo node
1497 1498 */
1498 1499 static int
1499 1500 get_state(dev_info_t *dip)
1500 1501 {
1501 1502 if (DEVI_IS_DEVICE_OFFLINE(dip) || DEVI_IS_DEVICE_DOWN(dip))
1502 1503 return (CT_DEV_EV_OFFLINE);
1503 1504 else if (DEVI_IS_DEVICE_DEGRADED(dip))
1504 1505 return (CT_DEV_EV_DEGRADED);
1505 1506 else
1506 1507 return (CT_DEV_EV_ONLINE);
1507 1508 }
1508 1509
1509 1510 /*
1510 1511 * Sets the current state of a device in a device contract
1511 1512 */
1512 1513 static void
1513 1514 set_cond_state(dev_info_t *dip)
1514 1515 {
1515 1516 uint_t state = get_state(dip);
1516 1517 cont_device_t *ctd;
1517 1518
1518 1519 /* verify that barrier is held */
1519 1520 ASSERT(ct_barrier_held(dip));
1520 1521
1521 1522 for (ctd = list_head(&(DEVI(dip)->devi_ct)); ctd != NULL;
1522 1523 ctd = list_next(&(DEVI(dip)->devi_ct), ctd)) {
1523 1524 mutex_enter(&ctd->cond_contract.ct_lock);
1524 1525 ASSERT(ctd->cond_dip == dip);
1525 1526 ctd->cond_state = state;
1526 1527 mutex_exit(&ctd->cond_contract.ct_lock);
1527 1528 }
1528 1529 }
1529 1530
1530 1531 /*
1531 1532 * Core routine called by event-specific routines when an event occurs.
1532 1533 * Determines if an event should be be published, and if it is to be
1533 1534 * published, whether a negotiation should take place. Also implements
1534 1535 * NEGEND events which publish the final disposition of an event after
1535 1536 * negotiations are complete.
1536 1537 *
1537 1538 * When an event occurs on a minor node, this routine walks the list of
1538 1539 * contracts hanging off a devinfo node and for each contract on the affected
1539 1540 * dip, evaluates the following cases
1540 1541 *
1541 1542 * a. an event that is synchronous, breaks the contract and NONEG not set
1542 1543 * - bumps up the outstanding negotiation counts on the dip
1543 1544 * - marks the dip as undergoing negotiation (devi_ct_neg)
1544 1545 * - event of type CTE_NEG is published
1545 1546 * b. an event that is synchronous, breaks the contract and NONEG is set
1546 1547 * - sets the final result to CT_NACK, event is blocked
1547 1548 * - does not publish an event
1548 1549 * c. event is asynchronous and breaks the contract
1549 1550 * - publishes a critical event irrespect of whether the NONEG
1550 1551 * flag is set, since the contract will be broken and contract
1551 1552 * owner needs to be informed.
1552 1553 * d. No contract breakage but the owner has subscribed to the event
1553 1554 * - publishes the event irrespective of the NONEG event as the
1554 1555 * owner has explicitly subscribed to the event.
1555 1556 * e. NEGEND event
1556 1557 * - publishes a critical event. Should only be doing this if
1557 1558 * if NONEG is not set.
1558 1559 * f. all other events
1559 1560 * - Since a contract is not broken and this event has not been
1560 1561 * subscribed to, this event does not need to be published for
1561 1562 * for this contract.
1562 1563 *
1563 1564 * Once an event is published, what happens next depends on the type of
1564 1565 * event:
1565 1566 *
1566 1567 * a. NEGEND event
1567 1568 * - cleanup all state associated with the preceding negotiation
1568 1569 * and return CT_ACK to the caller of contract_device_publish()
1569 1570 * b. NACKed event
1570 1571 * - One or more contracts had the NONEG term, so the event was
1571 1572 * blocked. Return CT_NACK to the caller.
1572 1573 * c. Negotiated event
1573 1574 * - Call wait_for_acks() to wait for responses from contract
1574 1575 * holders. The end result is either CT_ACK (event is permitted),
1575 1576 * CT_NACK (event is blocked) or CT_NONE (no contract owner)
1576 1577 * responded. This result is returned back to the caller.
1577 1578 * d. All other events
1578 1579 * - If the event was asynchronous (i.e. not negotiated) or
1579 1580 * a contract was not broken return CT_ACK to the caller.
1580 1581 */
1581 1582 static uint_t
1582 1583 contract_device_publish(dev_info_t *dip, dev_t dev, int spec_type,
1583 1584 uint_t evtype, nvlist_t *tnvl)
1584 1585 {
1585 1586 cont_device_t *ctd;
1586 1587 uint_t result = CT_NONE;
1587 1588 uint64_t evid = 0;
1588 1589 uint64_t nevid = 0;
1589 1590 char *path = NULL;
1590 1591 int negend;
1591 1592 int match;
1592 1593 int sync = 0;
1593 1594 contract_t *ct;
1594 1595 ct_kevent_t *event;
1595 1596 nvlist_t *nvl;
1596 1597 int broken = 0;
1597 1598
1598 1599 ASSERT(dip);
1599 1600 ASSERT(dev != NODEV && dev != DDI_DEV_T_NONE);
1600 1601 ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
1601 1602 (spec_type == S_IFBLK || spec_type == S_IFCHR));
1602 1603 ASSERT(evtype == 0 || (evtype & CT_DEV_ALLEVENT));
1603 1604
1604 1605 /* Is this a synchronous state change ? */
1605 1606 if (evtype != CT_EV_NEGEND) {
1606 1607 sync = is_sync_neg(get_state(dip), evtype);
1607 1608 /* NOP if unsupported transition */
1608 1609 if (sync == -2 || sync == -1) {
1609 1610 DEVI(dip)->devi_flags |= DEVI_CT_NOP;
1610 1611 result = (sync == -2) ? CT_ACK : CT_NONE;
1611 1612 goto out;
1612 1613 }
1613 1614 CT_DEBUG((CE_NOTE, "publish: is%s sync state change",
1614 1615 sync ? "" : " not"));
1615 1616 } else if (DEVI(dip)->devi_flags & DEVI_CT_NOP) {
1616 1617 DEVI(dip)->devi_flags &= ~DEVI_CT_NOP;
1617 1618 result = CT_ACK;
1618 1619 goto out;
1619 1620 }
1620 1621
1621 1622 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1622 1623 (void) ddi_pathname(dip, path);
1623 1624
1624 1625 mutex_enter(&(DEVI(dip)->devi_ct_lock));
1625 1626
1626 1627 /*
1627 1628 * Negotiation end - set the state of the device in the contract
1628 1629 */
1629 1630 if (evtype == CT_EV_NEGEND) {
1630 1631 CT_DEBUG((CE_NOTE, "publish: negend: setting cond state"));
1631 1632 set_cond_state(dip);
1632 1633 }
1633 1634
1634 1635 /*
1635 1636 * If this device didn't go through negotiation, don't publish
1636 1637 * a NEGEND event - simply release the barrier to allow other
1637 1638 * device events in.
1638 1639 */
1639 1640 negend = 0;
1640 1641 if (evtype == CT_EV_NEGEND && !DEVI(dip)->devi_ct_neg) {
1641 1642 CT_DEBUG((CE_NOTE, "publish: no negend reqd. release barrier"));
1642 1643 ct_barrier_release(dip);
1643 1644 mutex_exit(&(DEVI(dip)->devi_ct_lock));
1644 1645 result = CT_ACK;
1645 1646 goto out;
1646 1647 } else if (evtype == CT_EV_NEGEND) {
1647 1648 /*
1648 1649 * There are negotiated contract breakages that
1649 1650 * need a NEGEND event
1650 1651 */
1651 1652 ASSERT(ct_barrier_held(dip));
1652 1653 negend = 1;
1653 1654 CT_DEBUG((CE_NOTE, "publish: setting negend flag"));
1654 1655 } else {
1655 1656 /*
1656 1657 * This is a new event, not a NEGEND event. Wait for previous
1657 1658 * contract events to complete.
1658 1659 */
1659 1660 ct_barrier_acquire(dip);
1660 1661 }
1661 1662
1662 1663
1663 1664 match = 0;
1664 1665 for (ctd = list_head(&(DEVI(dip)->devi_ct)); ctd != NULL;
1665 1666 ctd = list_next(&(DEVI(dip)->devi_ct), ctd)) {
1666 1667
1667 1668 ctid_t ctid;
1668 1669 size_t len = strlen(path);
1669 1670
1670 1671 mutex_enter(&ctd->cond_contract.ct_lock);
1671 1672
1672 1673 ASSERT(ctd->cond_dip == dip);
1673 1674 ASSERT(ctd->cond_minor);
1674 1675 ASSERT(strncmp(ctd->cond_minor, path, len) == 0 &&
1675 1676 ctd->cond_minor[len] == ':');
1676 1677
1677 1678 if (dev != DDI_DEV_T_ANY && dev != ctd->cond_devt) {
1678 1679 mutex_exit(&ctd->cond_contract.ct_lock);
1679 1680 continue;
1680 1681 }
1681 1682 if (dev != DDI_DEV_T_ANY && spec_type != ctd->cond_spec) {
1682 1683 mutex_exit(&ctd->cond_contract.ct_lock);
1683 1684 continue;
1684 1685 }
1685 1686
1686 1687 /* We have a matching contract */
1687 1688 match = 1;
1688 1689 ctid = ctd->cond_contract.ct_id;
1689 1690 CT_DEBUG((CE_NOTE, "publish: found matching contract: %d",
1690 1691 ctid));
1691 1692
1692 1693 /*
1693 1694 * There are 4 possible cases
1694 1695 * 1. A contract is broken (dev not in acceptable state) and
1695 1696 * the state change is synchronous - start negotiation
1696 1697 * by sending a CTE_NEG critical event.
1697 1698 * 2. A contract is broken and the state change is
1698 1699 * asynchronous - just send a critical event and
1699 1700 * break the contract.
1700 1701 * 3. Contract is not broken, but consumer has subscribed
1701 1702 * to the event as a critical or informative event
1702 1703 * - just send the appropriate event
1703 1704 * 4. contract waiting for negend event - just send the critical
1704 1705 * NEGEND event.
1705 1706 */
1706 1707 broken = 0;
1707 1708 if (!negend && !(evtype & ctd->cond_aset)) {
1708 1709 broken = 1;
1709 1710 CT_DEBUG((CE_NOTE, "publish: Contract broken: %d",
1710 1711 ctid));
1711 1712 }
1712 1713
1713 1714 /*
1714 1715 * Don't send event if
1715 1716 * - contract is not broken AND
1716 1717 * - contract holder has not subscribed to this event AND
1717 1718 * - contract not waiting for a NEGEND event
1718 1719 */
1719 1720 if (!broken && !EVSENDP(ctd, evtype) &&
1720 1721 !ctd->cond_neg) {
1721 1722 CT_DEBUG((CE_NOTE, "contract_device_publish(): "
1722 1723 "contract (%d): no publish reqd: event %d",
1723 1724 ctd->cond_contract.ct_id, evtype));
1724 1725 mutex_exit(&ctd->cond_contract.ct_lock);
1725 1726 continue;
1726 1727 }
1727 1728
1728 1729 /*
1729 1730 * Note: need to kmem_zalloc() the event so mutexes are
1730 1731 * initialized automatically
1731 1732 */
1732 1733 ct = &ctd->cond_contract;
1733 1734 event = kmem_zalloc(sizeof (ct_kevent_t), KM_SLEEP);
1734 1735 event->cte_type = evtype;
1735 1736
1736 1737 if (broken && sync) {
1737 1738 CT_DEBUG((CE_NOTE, "publish: broken + sync: "
1738 1739 "ctid: %d", ctid));
1739 1740 ASSERT(!negend);
1740 1741 ASSERT(ctd->cond_currev_id == 0);
1741 1742 ASSERT(ctd->cond_currev_type == 0);
1742 1743 ASSERT(ctd->cond_currev_ack == 0);
1743 1744 ASSERT(ctd->cond_neg == 0);
1744 1745 if (ctd->cond_noneg) {
1745 1746 /* Nothing to publish. Event has been blocked */
1746 1747 CT_DEBUG((CE_NOTE, "publish: sync and noneg:"
1747 1748 "not publishing blocked ev: ctid: %d",
1748 1749 ctid));
1749 1750 result = CT_NACK;
1750 1751 kmem_free(event, sizeof (ct_kevent_t));
1751 1752 mutex_exit(&ctd->cond_contract.ct_lock);
1752 1753 continue;
1753 1754 }
1754 1755 event->cte_flags = CTE_NEG; /* critical neg. event */
1755 1756 ctd->cond_currev_type = event->cte_type;
1756 1757 ct_barrier_incr(dip);
1757 1758 DEVI(dip)->devi_ct_neg = 1; /* waiting for negend */
1758 1759 ctd->cond_neg = 1;
1759 1760 } else if (broken && !sync) {
1760 1761 CT_DEBUG((CE_NOTE, "publish: broken + async: ctid: %d",
1761 1762 ctid));
1762 1763 ASSERT(!negend);
1763 1764 ASSERT(ctd->cond_currev_id == 0);
1764 1765 ASSERT(ctd->cond_currev_type == 0);
1765 1766 ASSERT(ctd->cond_currev_ack == 0);
1766 1767 ASSERT(ctd->cond_neg == 0);
1767 1768 event->cte_flags = 0; /* critical event */
1768 1769 } else if (EVSENDP(ctd, event->cte_type)) {
1769 1770 CT_DEBUG((CE_NOTE, "publish: event suscrib: ctid: %d",
1770 1771 ctid));
1771 1772 ASSERT(!negend);
1772 1773 ASSERT(ctd->cond_currev_id == 0);
1773 1774 ASSERT(ctd->cond_currev_type == 0);
1774 1775 ASSERT(ctd->cond_currev_ack == 0);
1775 1776 ASSERT(ctd->cond_neg == 0);
1776 1777 event->cte_flags = EVINFOP(ctd, event->cte_type) ?
1777 1778 CTE_INFO : 0;
1778 1779 } else if (ctd->cond_neg) {
1779 1780 CT_DEBUG((CE_NOTE, "publish: NEGEND: ctid: %d", ctid));
1780 1781 ASSERT(negend);
1781 1782 ASSERT(ctd->cond_noneg == 0);
1782 1783 nevid = ctd->cond_contract.ct_nevent ?
1783 1784 ctd->cond_contract.ct_nevent->cte_id : 0;
1784 1785 ASSERT(ctd->cond_currev_id == nevid);
1785 1786 event->cte_flags = 0; /* NEGEND is always critical */
1786 1787 ctd->cond_currev_id = 0;
1787 1788 ctd->cond_currev_type = 0;
1788 1789 ctd->cond_currev_ack = 0;
1789 1790 ctd->cond_neg = 0;
1790 1791 } else {
1791 1792 CT_DEBUG((CE_NOTE, "publish: not publishing event for "
1792 1793 "ctid: %d, evtype: %d",
1793 1794 ctd->cond_contract.ct_id, event->cte_type));
1794 1795 ASSERT(!negend);
1795 1796 ASSERT(ctd->cond_currev_id == 0);
1796 1797 ASSERT(ctd->cond_currev_type == 0);
1797 1798 ASSERT(ctd->cond_currev_ack == 0);
1798 1799 ASSERT(ctd->cond_neg == 0);
1799 1800 kmem_free(event, sizeof (ct_kevent_t));
1800 1801 mutex_exit(&ctd->cond_contract.ct_lock);
1801 1802 continue;
1802 1803 }
1803 1804
1804 1805 nvl = NULL;
1805 1806 if (tnvl) {
1806 1807 VERIFY(nvlist_dup(tnvl, &nvl, 0) == 0);
1807 1808 if (negend) {
1808 1809 int32_t newct = 0;
1809 1810 ASSERT(ctd->cond_noneg == 0);
1810 1811 VERIFY(nvlist_add_uint64(nvl, CTS_NEVID, nevid)
1811 1812 == 0);
1812 1813 VERIFY(nvlist_lookup_int32(nvl, CTS_NEWCT,
1813 1814 &newct) == 0);
1814 1815 VERIFY(nvlist_add_int32(nvl, CTS_NEWCT,
1815 1816 newct == 1 ? 0 :
1816 1817 ctd->cond_contract.ct_id) == 0);
1817 1818 CT_DEBUG((CE_NOTE, "publish: negend: ctid: %d "
1818 1819 "CTS_NEVID: %llu, CTS_NEWCT: %s",
1819 1820 ctid, (unsigned long long)nevid,
1820 1821 newct ? "success" : "failure"));
1821 1822
1822 1823 }
1823 1824 }
1824 1825
1825 1826 if (ctd->cond_neg) {
1826 1827 ASSERT(ctd->cond_contract.ct_ntime.ctm_start == -1);
1827 1828 ASSERT(ctd->cond_contract.ct_qtime.ctm_start == -1);
1828 1829 ctd->cond_contract.ct_ntime.ctm_start = ddi_get_lbolt();
1829 1830 ctd->cond_contract.ct_qtime.ctm_start =
1830 1831 ctd->cond_contract.ct_ntime.ctm_start;
1831 1832 }
1832 1833
1833 1834 /*
1834 1835 * by holding the dip's devi_ct_lock we ensure that
1835 1836 * all ACK/NACKs are held up until we have finished
1836 1837 * publishing to all contracts.
1837 1838 */
1838 1839 mutex_exit(&ctd->cond_contract.ct_lock);
1839 1840 evid = cte_publish_all(ct, event, nvl, NULL);
1840 1841 mutex_enter(&ctd->cond_contract.ct_lock);
1841 1842
1842 1843 if (ctd->cond_neg) {
1843 1844 ASSERT(!negend);
1844 1845 ASSERT(broken);
1845 1846 ASSERT(sync);
1846 1847 ASSERT(!ctd->cond_noneg);
1847 1848 CT_DEBUG((CE_NOTE, "publish: sync break, setting evid"
1848 1849 ": %d", ctid));
1849 1850 ctd->cond_currev_id = evid;
1850 1851 } else if (negend) {
1851 1852 ctd->cond_contract.ct_ntime.ctm_start = -1;
1852 1853 ctd->cond_contract.ct_qtime.ctm_start = -1;
1853 1854 }
1854 1855 mutex_exit(&ctd->cond_contract.ct_lock);
1855 1856 }
1856 1857
1857 1858 /*
1858 1859 * If "negend" set counter back to initial state (-1) so that
1859 1860 * other events can be published. Also clear the negotiation flag
1860 1861 * on dip.
1861 1862 *
1862 1863 * 0 .. n are used for counting.
1863 1864 * -1 indicates counter is available for use.
1864 1865 */
1865 1866 if (negend) {
1866 1867 /*
1867 1868 * devi_ct_count not necessarily 0. We may have
1868 1869 * timed out in which case, count will be non-zero.
1869 1870 */
1870 1871 ct_barrier_release(dip);
1871 1872 DEVI(dip)->devi_ct_neg = 0;
1872 1873 CT_DEBUG((CE_NOTE, "publish: negend: reset dip state: dip=%p",
1873 1874 (void *)dip));
1874 1875 } else if (DEVI(dip)->devi_ct_neg) {
1875 1876 ASSERT(match);
1876 1877 ASSERT(!ct_barrier_empty(dip));
1877 1878 CT_DEBUG((CE_NOTE, "publish: sync count=%d, dip=%p",
1878 1879 DEVI(dip)->devi_ct_count, (void *)dip));
1879 1880 } else {
1880 1881 /*
1881 1882 * for non-negotiated events or subscribed events or no
1882 1883 * matching contracts
1883 1884 */
1884 1885 ASSERT(ct_barrier_empty(dip));
1885 1886 ASSERT(DEVI(dip)->devi_ct_neg == 0);
1886 1887 CT_DEBUG((CE_NOTE, "publish: async/non-nego/subscrib/no-match: "
1887 1888 "dip=%p", (void *)dip));
1888 1889
1889 1890 /*
1890 1891 * only this function when called from contract_device_negend()
1891 1892 * can reset the counter to READY state i.e. -1. This function
1892 1893 * is so called for every event whether a NEGEND event is needed
1893 1894 * or not, but the negend event is only published if the event
1894 1895 * whose end they signal is a negotiated event for the contract.
1895 1896 */
1896 1897 }
1897 1898
1898 1899 if (!match) {
1899 1900 /* No matching contracts */
1900 1901 CT_DEBUG((CE_NOTE, "publish: No matching contract"));
1901 1902 result = CT_NONE;
1902 1903 } else if (result == CT_NACK) {
1903 1904 /* a non-negotiable contract exists and this is a neg. event */
1904 1905 CT_DEBUG((CE_NOTE, "publish: found 1 or more NONEG contract"));
1905 1906 (void) wait_for_acks(dip, dev, spec_type, evtype);
1906 1907 } else if (DEVI(dip)->devi_ct_neg) {
1907 1908 /* one or more contracts going through negotations */
1908 1909 CT_DEBUG((CE_NOTE, "publish: sync contract: waiting"));
1909 1910 result = wait_for_acks(dip, dev, spec_type, evtype);
1910 1911 } else {
1911 1912 /* no negotiated contracts or no broken contracts or NEGEND */
1912 1913 CT_DEBUG((CE_NOTE, "publish: async/no-break/negend"));
1913 1914 result = CT_ACK;
1914 1915 }
1915 1916
1916 1917 /*
1917 1918 * Release the lock only now so that the only point where we
1918 1919 * drop the lock is in wait_for_acks(). This is so that we don't
1919 1920 * miss cv_signal/cv_broadcast from contract holders
1920 1921 */
1921 1922 CT_DEBUG((CE_NOTE, "publish: dropping devi_ct_lock"));
1922 1923 mutex_exit(&(DEVI(dip)->devi_ct_lock));
1923 1924
1924 1925 out:
1925 1926 nvlist_free(tnvl);
1926 1927 if (path)
1927 1928 kmem_free(path, MAXPATHLEN);
1928 1929
1929 1930
1930 1931 CT_DEBUG((CE_NOTE, "publish: result = %s", result_str(result)));
1931 1932 return (result);
1932 1933 }
1933 1934
1934 1935
1935 1936 /*
1936 1937 * contract_device_offline
1937 1938 *
1938 1939 * Event publishing routine called by I/O framework when a device is offlined.
1939 1940 */
1940 1941 ct_ack_t
1941 1942 contract_device_offline(dev_info_t *dip, dev_t dev, int spec_type)
1942 1943 {
1943 1944 nvlist_t *nvl;
1944 1945 uint_t result;
1945 1946 uint_t evtype;
1946 1947
1947 1948 VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1948 1949
1949 1950 evtype = CT_DEV_EV_OFFLINE;
1950 1951 result = contract_device_publish(dip, dev, spec_type, evtype, nvl);
1951 1952
1952 1953 /*
1953 1954 * If a contract offline is NACKED, the framework expects us to call
1954 1955 * NEGEND ourselves, since we know the final result
1955 1956 */
1956 1957 if (result == CT_NACK) {
1957 1958 contract_device_negend(dip, dev, spec_type, CT_EV_FAILURE);
1958 1959 }
1959 1960
1960 1961 return (result);
1961 1962 }
1962 1963
1963 1964 /*
1964 1965 * contract_device_degrade
1965 1966 *
1966 1967 * Event publishing routine called by I/O framework when a device
1967 1968 * moves to degrade state.
1968 1969 */
1969 1970 /*ARGSUSED*/
1970 1971 void
1971 1972 contract_device_degrade(dev_info_t *dip, dev_t dev, int spec_type)
1972 1973 {
1973 1974 nvlist_t *nvl;
1974 1975 uint_t evtype;
1975 1976
1976 1977 VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1977 1978
1978 1979 evtype = CT_DEV_EV_DEGRADED;
1979 1980 (void) contract_device_publish(dip, dev, spec_type, evtype, nvl);
1980 1981 }
1981 1982
1982 1983 /*
1983 1984 * contract_device_undegrade
1984 1985 *
1985 1986 * Event publishing routine called by I/O framework when a device
1986 1987 * moves from degraded state to online state.
1987 1988 */
1988 1989 /*ARGSUSED*/
1989 1990 void
1990 1991 contract_device_undegrade(dev_info_t *dip, dev_t dev, int spec_type)
1991 1992 {
1992 1993 nvlist_t *nvl;
1993 1994 uint_t evtype;
1994 1995
1995 1996 VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1996 1997
1997 1998 evtype = CT_DEV_EV_ONLINE;
1998 1999 (void) contract_device_publish(dip, dev, spec_type, evtype, nvl);
1999 2000 }
2000 2001
2001 2002 /*
2002 2003 * For all contracts which have undergone a negotiation (because the device
2003 2004 * moved out of the acceptable state for that contract and the state
2004 2005 * change is synchronous i.e. requires negotiation) this routine publishes
2005 2006 * a CT_EV_NEGEND event with the final disposition of the event.
2006 2007 *
2007 2008 * This event is always a critical event.
2008 2009 */
2009 2010 void
2010 2011 contract_device_negend(dev_info_t *dip, dev_t dev, int spec_type, int result)
2011 2012 {
2012 2013 nvlist_t *nvl;
2013 2014 uint_t evtype;
2014 2015
2015 2016 ASSERT(result == CT_EV_SUCCESS || result == CT_EV_FAILURE);
2016 2017
2017 2018 CT_DEBUG((CE_NOTE, "contract_device_negend(): entered: result: %d, "
2018 2019 "dip: %p", result, (void *)dip));
2019 2020
2020 2021 VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2021 2022 VERIFY(nvlist_add_int32(nvl, CTS_NEWCT,
2022 2023 result == CT_EV_SUCCESS ? 1 : 0) == 0);
2023 2024
2024 2025 evtype = CT_EV_NEGEND;
2025 2026 (void) contract_device_publish(dip, dev, spec_type, evtype, nvl);
2026 2027
2027 2028 CT_DEBUG((CE_NOTE, "contract_device_negend(): exit dip: %p",
2028 2029 (void *)dip));
2029 2030 }
2030 2031
2031 2032 /*
2032 2033 * Wrapper routine called by other subsystems (such as LDI) to start
2033 2034 * negotiations when a synchronous device state change occurs.
2034 2035 * Returns CT_ACK or CT_NACK.
2035 2036 */
2036 2037 ct_ack_t
2037 2038 contract_device_negotiate(dev_info_t *dip, dev_t dev, int spec_type,
2038 2039 uint_t evtype)
2039 2040 {
2040 2041 int result;
2041 2042
2042 2043 ASSERT(dip);
2043 2044 ASSERT(dev != NODEV);
2044 2045 ASSERT(dev != DDI_DEV_T_ANY);
2045 2046 ASSERT(dev != DDI_DEV_T_NONE);
↓ open down ↓ |
2013 lines elided |
↑ open up ↑ |
2046 2047 ASSERT(spec_type == S_IFBLK || spec_type == S_IFCHR);
2047 2048
2048 2049 switch (evtype) {
2049 2050 case CT_DEV_EV_OFFLINE:
2050 2051 result = contract_device_offline(dip, dev, spec_type);
2051 2052 break;
2052 2053 default:
2053 2054 cmn_err(CE_PANIC, "contract_device_negotiate(): Negotiation "
2054 2055 "not supported: event (%d) for dev_t (%lu) and spec (%d), "
2055 2056 "dip (%p)", evtype, dev, spec_type, (void *)dip);
2056 - result = CT_NACK;
2057 2057 break;
2058 2058 }
2059 2059
2060 2060 return (result);
2061 2061 }
2062 2062
2063 2063 /*
2064 2064 * A wrapper routine called by other subsystems (such as the LDI) to
2065 2065 * finalize event processing for a state change event. For synchronous
2066 2066 * state changes, this publishes NEGEND events. For asynchronous i.e.
2067 2067 * non-negotiable events this publishes the event.
2068 2068 */
2069 2069 void
2070 2070 contract_device_finalize(dev_info_t *dip, dev_t dev, int spec_type,
2071 2071 uint_t evtype, int ct_result)
2072 2072 {
2073 2073 ASSERT(dip);
2074 2074 ASSERT(dev != NODEV);
2075 2075 ASSERT(dev != DDI_DEV_T_ANY);
2076 2076 ASSERT(dev != DDI_DEV_T_NONE);
2077 2077 ASSERT(spec_type == S_IFBLK || spec_type == S_IFCHR);
2078 2078
2079 2079 switch (evtype) {
2080 2080 case CT_DEV_EV_OFFLINE:
2081 2081 contract_device_negend(dip, dev, spec_type, ct_result);
2082 2082 break;
2083 2083 case CT_DEV_EV_DEGRADED:
2084 2084 contract_device_degrade(dip, dev, spec_type);
2085 2085 contract_device_negend(dip, dev, spec_type, ct_result);
2086 2086 break;
2087 2087 case CT_DEV_EV_ONLINE:
2088 2088 contract_device_undegrade(dip, dev, spec_type);
2089 2089 contract_device_negend(dip, dev, spec_type, ct_result);
2090 2090 break;
2091 2091 default:
2092 2092 cmn_err(CE_PANIC, "contract_device_finalize(): Unsupported "
2093 2093 "event (%d) for dev_t (%lu) and spec (%d), dip (%p)",
2094 2094 evtype, dev, spec_type, (void *)dip);
2095 2095 break;
2096 2096 }
2097 2097 }
2098 2098
2099 2099 /*
2100 2100 * Called by I/O framework when a devinfo node is freed to remove the
2101 2101 * association between a devinfo node and its contracts.
2102 2102 */
2103 2103 void
2104 2104 contract_device_remove_dip(dev_info_t *dip)
2105 2105 {
2106 2106 cont_device_t *ctd;
2107 2107 cont_device_t *next;
2108 2108 contract_t *ct;
2109 2109
2110 2110 mutex_enter(&(DEVI(dip)->devi_ct_lock));
2111 2111 ct_barrier_wait_for_release(dip);
2112 2112
2113 2113 for (ctd = list_head(&(DEVI(dip)->devi_ct)); ctd != NULL; ctd = next) {
2114 2114 next = list_next(&(DEVI(dip)->devi_ct), ctd);
2115 2115 list_remove(&(DEVI(dip)->devi_ct), ctd);
2116 2116 ct = &ctd->cond_contract;
2117 2117 /*
2118 2118 * Unlink the dip associated with this contract
2119 2119 */
2120 2120 mutex_enter(&ct->ct_lock);
2121 2121 ASSERT(ctd->cond_dip == dip);
2122 2122 ctd->cond_dip = NULL; /* no longer linked to dip */
2123 2123 contract_rele(ct); /* remove hold for dip linkage */
2124 2124 CT_DEBUG((CE_NOTE, "ct: remove_dip: removed dip from contract: "
2125 2125 "ctid: %d", ct->ct_id));
2126 2126 mutex_exit(&ct->ct_lock);
2127 2127 }
2128 2128 ASSERT(list_is_empty(&(DEVI(dip)->devi_ct)));
2129 2129 mutex_exit(&(DEVI(dip)->devi_ct_lock));
2130 2130 }
2131 2131
2132 2132 /*
2133 2133 * Barrier related routines
2134 2134 */
2135 2135 static void
2136 2136 ct_barrier_acquire(dev_info_t *dip)
2137 2137 {
2138 2138 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2139 2139 CT_DEBUG((CE_NOTE, "ct_barrier_acquire: waiting for barrier"));
2140 2140 while (DEVI(dip)->devi_ct_count != -1)
2141 2141 cv_wait(&(DEVI(dip)->devi_ct_cv), &(DEVI(dip)->devi_ct_lock));
2142 2142 DEVI(dip)->devi_ct_count = 0;
2143 2143 CT_DEBUG((CE_NOTE, "ct_barrier_acquire: thread owns barrier"));
2144 2144 }
2145 2145
2146 2146 static void
2147 2147 ct_barrier_release(dev_info_t *dip)
2148 2148 {
2149 2149 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2150 2150 ASSERT(DEVI(dip)->devi_ct_count != -1);
2151 2151 DEVI(dip)->devi_ct_count = -1;
2152 2152 cv_broadcast(&(DEVI(dip)->devi_ct_cv));
2153 2153 CT_DEBUG((CE_NOTE, "ct_barrier_release: Released barrier"));
2154 2154 }
2155 2155
2156 2156 static int
2157 2157 ct_barrier_held(dev_info_t *dip)
2158 2158 {
2159 2159 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2160 2160 return (DEVI(dip)->devi_ct_count != -1);
2161 2161 }
2162 2162
2163 2163 static int
2164 2164 ct_barrier_empty(dev_info_t *dip)
2165 2165 {
2166 2166 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2167 2167 ASSERT(DEVI(dip)->devi_ct_count != -1);
2168 2168 return (DEVI(dip)->devi_ct_count == 0);
2169 2169 }
2170 2170
2171 2171 static void
2172 2172 ct_barrier_wait_for_release(dev_info_t *dip)
2173 2173 {
2174 2174 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2175 2175 while (DEVI(dip)->devi_ct_count != -1)
2176 2176 cv_wait(&(DEVI(dip)->devi_ct_cv), &(DEVI(dip)->devi_ct_lock));
2177 2177 }
2178 2178
2179 2179 static void
2180 2180 ct_barrier_decr(dev_info_t *dip)
2181 2181 {
2182 2182 CT_DEBUG((CE_NOTE, "barrier_decr: ct_count before decr: %d",
2183 2183 DEVI(dip)->devi_ct_count));
2184 2184
2185 2185 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2186 2186 ASSERT(DEVI(dip)->devi_ct_count > 0);
2187 2187
2188 2188 DEVI(dip)->devi_ct_count--;
2189 2189 if (DEVI(dip)->devi_ct_count == 0) {
2190 2190 cv_broadcast(&DEVI(dip)->devi_ct_cv);
2191 2191 CT_DEBUG((CE_NOTE, "barrier_decr: cv_broadcast"));
2192 2192 }
2193 2193 }
2194 2194
2195 2195 static void
2196 2196 ct_barrier_incr(dev_info_t *dip)
2197 2197 {
2198 2198 ASSERT(ct_barrier_held(dip));
2199 2199 DEVI(dip)->devi_ct_count++;
2200 2200 }
2201 2201
2202 2202 static int
2203 2203 ct_barrier_wait_for_empty(dev_info_t *dip, int secs)
2204 2204 {
2205 2205 clock_t abstime;
2206 2206
2207 2207 ASSERT(MUTEX_HELD(&(DEVI(dip)->devi_ct_lock)));
2208 2208
2209 2209 abstime = ddi_get_lbolt() + drv_usectohz(secs*1000000);
2210 2210 while (DEVI(dip)->devi_ct_count) {
2211 2211 if (cv_timedwait(&(DEVI(dip)->devi_ct_cv),
2212 2212 &(DEVI(dip)->devi_ct_lock), abstime) == -1) {
2213 2213 return (-1);
2214 2214 }
2215 2215 }
2216 2216 return (0);
2217 2217 }
↓ open down ↓ |
151 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX