/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <sys/id_space.h>
#include <sys/sysmacros.h>
#include <sys/contract.h>
#include <sys/contract_impl.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_implfuncs.h>
/*
* Device Contracts
* -----------------
* This file contains the core code for the device contracts framework.
* A device contract is an agreement or a contract between a process and
* the kernel regarding the state of the device. A device contract may be
* created when a relationship is formed between a device and a process
* i.e. at open(2) time, or it may be created at some point after the device
* has been opened. A device contract once formed may be broken by either party.
* A device contract can be broken by the process by an explicit abandon of the
* contract or by an implicit abandon when the process exits. A device contract
* can be broken by the kernel either asynchronously (without negotiation) or
* synchronously (with negotiation). Exactly which happens depends on the device
* state transition. The following state diagram shows the transitions between
* device states. Only device state transitions currently supported by device
* contracts is shown.
*
* <-- A -->
* /-----------------> DEGRADED
* | |
* | |
* | | S
* | | |
* | | v
* v S --> v
* ONLINE ------------> OFFLINE
*
*
* In the figure above, the arrows indicate the direction of transition. The
* letter S refers to transitions which are inherently synchronous i.e.
* require negotiation and the letter A indicates transitions which are
* asynchronous i.e. are done without contract negotiations. A good example
* of a synchronous transition is the ONLINE -> OFFLINE transition. This
* transition cannot happen as long as there are consumers which have the
* device open. Thus some form of negotiation needs to happen between the
* consumers and the kernel to ensure that consumers either close devices
* or disallow the move to OFFLINE. Certain other transitions such as
* ONLINE --> DEGRADED for example, are inherently asynchronous i.e.
* non-negotiable. A device that suffers a fault that degrades its
* capabilities will become degraded irrespective of what consumers it has,
* so a negotiation in this case is pointless.
*
* The following device states are currently defined for device contracts:
*
* CT_DEV_EV_ONLINE
* The device is online and functioning normally
* CT_DEV_EV_DEGRADED
* The device is online but is functioning in a degraded capacity
* CT_DEV_EV_OFFLINE
* The device is offline and is no longer configured
*
* A typical consumer of device contracts starts out with a contract
* template and adds terms to that template. These include the
* "acceptable set" (A-set) term, which is a bitset of device states which
* are guaranteed by the contract. If the device moves out of a state in
* the A-set, the contract is broken. The breaking of the contract can
* be asynchronous in which case a critical contract event is sent to the
* contract holder but no negotiations take place. If the breaking of the
* contract is synchronous, negotations are opened between the affected
* consumer and the kernel. The kernel does this by sending a critical
* event to the consumer with the CTE_NEG flag set indicating that this
* is a negotiation event. The consumer can accept this change by sending
* a ACK message to the kernel. Alternatively, if it has the necessary
* privileges, it can send a NACK message to the kernel which will block
* the device state change. To NACK a negotiable event, a process must
* have the {PRIV_SYS_DEVICES} privilege asserted in its effective set.
*
* Other terms include the "minor path" term, specified explicitly if the
* contract is not being created at open(2) time or specified implicitly
* if the contract is being created at open time via an activated template.
*
* A contract event is sent on any state change to which the contract
* owner has subscribed via the informative or critical event sets. Only
* critical events are guaranteed to be delivered. Since all device state
* changes are controlled by the kernel and cannot be arbitrarily generated
* by a non-privileged user, the {PRIV_CONTRACT_EVENT} privilege does not
* need to be asserted in a process's effective set to designate an event as
* critical. To ensure privacy, a process must either have the same effective
* userid as the contract holder or have the {PRIV_CONTRACT_OBSERVER} privilege
* asserted in its effective set in order to observe device contract events
* off the device contract type specific endpoint.
*
* Yet another term available with device contracts is the "non-negotiable"
* term. This term is used to pre-specify a NACK to any contract negotiation.
* This term is ignored for asynchronous state changes. For example, a
* provcess may have the A-set {ONLINE|DEGRADED} and make the contract
* non-negotiable. In this case, the device contract framework assumes a
* NACK for any transition to OFFLINE and blocks the offline. If the A-set
* is {ONLINE} and the non-negotiable term is set, transitions to OFFLINE
* are NACKed but transitions to DEGRADE succeed.
*
* The OFFLINE negotiation (if OFFLINE state is not in the A-set for a contract)
* happens just before the I/O framework attempts to offline a device
* (i.e. detach a device and set the offline flag so that it cannot be
* reattached). A device contract holder is expected to either NACK the offline
* (if privileged) or release the device and allow the offline to proceed.
*
* The DEGRADE contract event (if DEGRADE is not in the A-set for a contract)
* is generated just before the I/O framework transitions the device state
* to "degraded" (i.e. DEVI_DEVICE_DEGRADED in I/O framework terminology).
*
* The contract holder is expected to ACK or NACK a negotiation event
* within the timeout period, the device contract framework will behave
* as if the contract does not exist and will proceed with the event.
*
* Unlike a process contract a device contract does not need to exist
* once it is abandoned, since it does not define a fault boundary. It
* merely represents an agreement between a process and the kernel
* regarding the state of the device. Once the process has abandoned
* the contract (either implicitly via a process exit or explicitly)
* the kernel has no reason to retain the contract. As a result
* device contracts are neither inheritable nor need to exist in an
* orphan state.
*
* A device unlike a process may exist in multiple contracts and has
* a "life" outside a device contract. A device unlike a process
* may exist without an associated contract. Unlike a process contract
* a device contract may be formed after a binding relationship is
* formed between a process and a device.
*
* IMPLEMENTATION NOTES
* ====================
* DATA STRUCTURES
* ----------------
* The heart of the device contracts implementation is the device contract
* private cont_device_t (or ctd for short) data structure. It encapsulates
* the generic contract_t data structure and has a number of private
* fields.
* These include:
* cond_minor: The minor device that is the subject of the contract
* cond_aset: The bitset of states which are guaranteed by the
* contract
* cond_noneg: If set, indicates that the result of negotiation has
* been predefined to be a NACK
* In addition, there are other device identifiers such the devinfo node,
* dev_t and spec_type of the minor node. There are also a few fields that
* are used during negotiation to maintain state. See
* for details.
* The ctd structure represents the device private part of a contract of
* type "device"
*
* Another data structure used by device contracts is ctmpl_device. It is
* the device contracts private part of the contract template structure. It
* encapsulates the generic template structure "ct_template_t" and includes
* the following device contract specific fields
* ctd_aset: The bitset of states that should be guaranteed by a
* contract
* ctd_noneg: If set, indicates that contract should NACK a
* negotiation
* ctd_minor: The devfs_path (without the /devices prefix) of the
* minor node that is the subject of the contract.
*
* ALGORITHMS
* ---------
* There are three sets of routines in this file
* Template related routines
* -------------------------
* These routines provide support for template related operations initated
* via the generic template operations. These include routines that dup
* a template, free it, and set various terms in the template
* (such as the minor node path, the acceptable state set (or A-set)
* and the non-negotiable term) as well as a routine to query the
* device specific portion of the template for the abovementioned terms.
* There is also a routine to create (ctmpl_device_create) that is used to
* create a contract from a template. This routine calls (after initial
* setup) the common function used to create a device contract
* (contract_device_create).
*
* core device contract implementation
* ----------------------------------
* These routines support the generic contract framework to provide
* functionality that allows contracts to be created, managed and
* destroyed. The contract_device_create() routine is a routine used
* to create a contract from a template (either via an explicit create
* operation on a template or implicitly via an open with an
* activated template.). The contract_device_free() routine assists
* in freeing the device contract specific parts. There are routines
* used to abandon (contract_device_abandon) a device contract as well
* as a routine to destroy (which despite its name does not destroy,
* it only moves a contract to a dead state) a contract.
* There is also a routine to return status information about a
* contract - the level of detail depends on what is requested by the
* user. A value of CTD_FIXED only returns fixed length fields such
* as the A-set, state of device and value of the "noneg" term. If
* CTD_ALL is specified, the minor node path is returned as well.
*
* In addition there are interfaces (contract_device_ack/nack) which
* are used to support negotiation between userland processes and
* device contracts. These interfaces record the acknowledgement
* or lack thereof for negotiation events and help determine if the
* negotiated event should occur.
*
* "backend routines"
* -----------------
* The backend routines form the interface between the I/O framework
* and the device contract subsystem. These routines, allow the I/O
* framework to call into the device contract subsystem to notify it of
* impending changes to a device state as well as to inform of the
* final disposition of such attempted state changes. Routines in this
* class include contract_device_offline() that indicates an attempt to
* offline a device, contract_device_degrade() that indicates that
* a device is moving to the degraded state and contract_device_negend()
* that is used by the I/O framework to inform the contracts subsystem of
* the final disposition of an attempted operation.
*
* SUMMARY
* -------
* A contract starts its life as a template. A process allocates a device
* contract template and sets various terms:
* The A-set
* The device minor node
* Critical and informative events
* The noneg i.e. no negotition term
* Setting of these terms in the template is done via the
* ctmpl_device_set() entry point in this file. A process can query a
* template to determine the terms already set in the template - this is
* facilitated by the ctmpl_device_get() routine.
*
* Once all the appropriate terms are set, the contract is instantiated via
* one of two methods
* - via an explicit create operation - this is facilitated by the
* ctmpl_device_create() entry point
* - synchronously with the open(2) system call - this is achieved via the
* contract_device_open() routine.
* The core work for both these above functions is done by
* contract_device_create()
*
* A contract once created can be queried for its status. Support for
* status info is provided by both the common contracts framework and by
* the "device" contract type. If the level of detail requested is
* CTD_COMMON, only the common contract framework data is used. Higher
* levels of detail result in calls to contract_device_status() to supply
* device contract type specific status information.
*
* A contract once created may be abandoned either explicitly or implictly.
* In either case, the contract_device_abandon() function is invoked. This
* function merely calls contract_destroy() which moves the contract to
* the DEAD state. The device contract portion of destroy processing is
* provided by contract_device_destroy() which merely disassociates the
* contract from its device devinfo node. A contract in the DEAD state is
* not freed. It hanbgs around until all references to the contract are
* gone. When that happens, the contract is finally deallocated. The
* device contract specific portion of the free is done by
* contract_device_free() which finally frees the device contract specific
* data structure (cont_device_t).
*
* When a device undergoes a state change, the I/O framework calls the
* corresponding device contract entry point. For example, when a device
* is about to go OFFLINE, the routine contract_device_offline() is
* invoked. Similarly if a device moves to DEGRADED state, the routine
* contract_device_degrade() function is called. These functions call the
* core routine contract_device_publish(). This function determines via
* the function is_sync_neg() whether an event is a synchronous (i.e.
* negotiable) event or not. In the former case contract_device_publish()
* publishes a CTE_NEG event and then waits in wait_for_acks() for ACKs
* publishes the event and does not wait. In the negotiation case, ACKs or
* NACKs from userland consumers results in contract_device_ack_nack()
* being called where the result of the negotiation is recorded in the
* contract data structure. Once all outstanding contract owners have
* responded, the device contract code in wait_for_acks() determines the
* final result of the negotiation. A single NACK overrides all other ACKs
* If there is no NACK, then a single ACK will result in an overall ACK
* result. If there are no ACKs or NACKs, then the result CT_NONE is
* returned back to the I/O framework. Once the event is permitted or
* blocked, the I/O framework proceeds or aborts the state change. The
* I/O framework then calls contract_device_negend() with a result code
* indicating final disposition of the event. This call releases the
* barrier and other state associated with the previous negotiation,
* which permits the next event (if any) to come into the device contract
* framework.
*
* Finally, a device that has outstanding contracts may be removed from
* the system which results in its devinfo node being freed. The devinfo
* free routine in the I/O framework, calls into the device contract
* function - contract_device_remove_dip(). This routine, disassociates
* the dip from all contracts associated with the contract being freed,
* allowing the devinfo node to be freed.
*
* LOCKING
* ---------
* There are four sets of data that need to be protected by locks
*
* i) device contract specific portion of the contract template - This data
* is protected by the template lock ctmpl_lock.
*
* ii) device contract specific portion of the contract - This data is
* protected by the contract lock ct_lock
*
* iii) The linked list of contracts hanging off a devinfo node - This
* list is protected by the per-devinfo node lock devi_ct_lock
*
* iv) Finally there is a barrier, controlled by devi_ct_lock, devi_ct_cv
* and devi_ct_count that controls state changes to a dip
*
* The template lock is independent in that none of the other locks in this
* file may be taken while holding the template lock (and vice versa).
*
* The remaining three locks have the following lock order
*
* devi_ct_lock -> ct_count barrier -> ct_lock
*
*/
/* barrier routines */
/*
* Macro predicates for determining when events should be sent and how.
*/
/*
* State transition table showing which transitions are synchronous and which
* are not.
*/
struct ct_dev_negtable {
} ct_dev_negtable[] = {
{0}
};
/*
* Device contract template implementation
*/
/*
* ctmpl_device_dup
*
* The device contract template dup entry point.
* This simply copies all the fields (generic as well as device contract
* specific) fields of the original.
*/
static struct ct_template *
{
char *buf;
char *minor;
/*
* copy generic fields.
* ctmpl_copy returns with old template lock held
*/
} else {
}
if (buf) {
} else {
}
if (minor) {
}
}
/*
* ctmpl_device_free
*
* The device contract template free entry point. Just
* frees the template.
*/
static void
{
}
/*
* SAFE_EV is the set of events which a non-privileged process is
* allowed to make critical. An unprivileged device contract owner has
* no control over when a device changes state, so all device events
* can be in the critical set.
*
* EXCESS tells us if "value", a critical event set, requires
* additional privilege. For device contracts EXCESS currently
* evaluates to 0.
*/
/*
* ctmpl_device_set
*
* The device contract template set entry point. Sets various terms in the
* template. The non-negotiable term can only be set if the process has
* the {PRIV_SYS_DEVICES} privilege asserted in its effective set.
*/
static int
{
int error;
int spec_type;
char *str_value;
} else {
return (EINVAL);
}
case CTDP_ACCEPT:
if (param_value & ~CT_DEV_ALLEVENT)
return (EINVAL);
if (param_value == 0)
return (EINVAL);
if (param_value == CT_DEV_ALLEVENT)
return (EINVAL);
break;
case CTDP_NONEG:
if (param_value != CTDP_NONEG_SET &&
return (EINVAL);
/*
* only privileged processes can designate a contract
* non-negotiatble.
*/
if (param_value == CTDP_NONEG_SET &&
return (error);
}
break;
case CTDP_MINOR:
if (*str_value != '/' ||
strlen("/devices/")) == 0 ||
return (EINVAL);
}
spec_type = 0;
return (ERANGE);
}
return (EINVAL);
}
}
break;
case CTP_EV_CRITICAL:
/*
* Currently for device contracts, any event
* may be added to the critical set. We retain the
* following code however for future enhancements.
*/
if (EXCESS(param_value) &&
return (error);
break;
default:
return (EINVAL);
}
return (0);
}
/*
* ctmpl_device_get
*
* The device contract template get entry point. Simply fetches and
* returns the value of the requested term.
*/
static int
{
return (EINVAL);
}
case CTDP_ACCEPT:
break;
case CTDP_NONEG:
break;
case CTDP_MINOR:
} else {
return (ENOENT);
}
break;
default:
return (EINVAL);
}
return (0);
}
/*
* Device contract type specific portion of creating a contract using
* a specified template
*/
/*ARGSUSED*/
int
{
char *buf;
int spec_type;
int error;
return (EINVAL);
/* incomplete template */
return (EINVAL);
} else {
}
spec_type = 0;
"tmpl_create: failed to find device: %s", buf));
return (ERANGE);
}
"process (%d) with device (devt = %lu, spec_type = %s)",
return (error);
}
return (0);
}
/*
* Device contract specific template entry points
*/
ctmpl_device_dup, /* ctop_dup */
ctmpl_device_free, /* ctop_free */
ctmpl_device_set, /* ctop_set */
ctmpl_device_get, /* ctop_get */
ctmpl_device_create, /* ctop_create */
CT_DEV_ALLEVENT /* all device events bitmask */
};
/*
* Device contract implementation
*/
/*
* contract_device_default
*
* The device contract default template entry point. Creates a
* device contract template with a default A-set and no "noneg" ,
* with informative degrade events and critical offline events.
* There is no default minor path.
*/
static ct_template_t *
contract_device_default(void)
{
}
/*
* contract_device_free
*
* Destroys the device contract specific portion of a contract and
* frees the contract.
*/
static void
{
}
/*
* contract_device_abandon
*
* The device contract abandon entry point.
*/
static void
{
/*
* device contracts cannot be inherited or orphaned.
* Move the contract to the DEAD_STATE. It will be freed
* once all references to it are gone.
*/
}
/*
* contract_device_destroy
*
* The device contract destroy entry point.
* Called from contract_destroy() to do any type specific destroy. Note
* that destroy is a misnomer - this does not free the contract, it only
* moves it to the dead state. A contract is actually freed via
* contract_rele() -> contract_dtor(), contop_free()
*/
static void
{
for (;;) {
/*
* The dip has been removed, this is a dangling contract
* Check that dip linkages are NULL
*/
" contract has no devinfo node. contract ctid : %d",
return;
}
/*
* The intended lock order is : devi_ct_lock -> ct_count
* barrier -> ct_lock.
* However we can't do this here as dropping the ct_lock allows
* a race condition with i_ddi_free_node()/
* contract_device_remove_dip() which may free off dip before
* we can take devi_ct_lock. So use mutex_tryenter to avoid
* dropping ct_lock until we have acquired devi_ct_lock.
*/
break;
}
/*
* Waiting for the barrier to be released is strictly speaking not
* necessary. But it simplifies the implementation of
* contract_device_publish() by establishing the invariant that
* device contracts cannot go away during negotiation.
*/
}
/*
* contract_device_status
*
* The device contract status entry point. Called when level of "detail"
* is either CTD_FIXED or CTD_ALL
*
*/
static void
{
/*
* There's no need to hold the contract lock while accessing static
* data like aset or noneg. But since we need the lock to access other
* data like state, we hold it anyway.
*/
return;
}
}
/*
* Converts a result integer into the corresponding string. Used for printing
* messages
*/
static char *
{
switch (result) {
case CT_ACK:
return ("CT_ACK");
case CT_NACK:
return ("CT_NACK");
case CT_NONE:
return ("CT_NONE");
default:
return ("UNKNOWN");
}
}
/*
* Converts a device state integer constant into the corresponding string.
* Used to print messages.
*/
static char *
{
switch (state) {
case CT_DEV_EV_ONLINE:
return ("ONLINE");
case CT_DEV_EV_DEGRADED:
return ("DEGRADED");
case CT_DEV_EV_OFFLINE:
return ("OFFLINE");
default:
return ("UNKNOWN");
}
}
/*
* Routine that determines if a particular CT_DEV_EV_? event corresponds to a
* synchronous state change or not.
*/
static int
{
int i;
return (-2);
}
for (i = 0; ct_dev_negtable[i].st_new != 0; i++) {
return (ct_dev_negtable[i].st_neg);
}
}
return (-1);
}
/*
* Used to cleanup cached dv_nodes so that when a device is released by
* a contract holder, its devinfo node can be successfully detached.
*/
static int
{
char *devnm;
/* pdip can be NULL if we have contracts against the root dip */
char *path;
"device=%s", path));
return (EDEADLOCK);
}
if (pdip) {
} else {
}
return (0);
}
/*
* Endpoint of a ct_ctl_ack() or ct_ctl_nack() call from userland.
* Results in the ACK or NACK being recorded on the dip for one particular
* contracts against a device to determine if a particular device state change
* should be allowed.
*/
static int
{
int error;
/*
* Negotiation only if new state is not in A-set
*/
/*
* Negotiation only if transition is synchronous
*/
/*
* We shouldn't be negotiating if the "noneg" flag is set
*/
if (dip)
/*
* dv_clean only if !NACK and offline state change
*/
if (error != 0) {
ctid));
}
}
if (dip)
"(type=%s, id=%llu) on removed device",
ctid));
} else {
"ctid: %d", ctid));
}
return (error);
}
/*
* Must follow lock order: devi_ct_lock -> ct_count barrier - >ct_lock
*/
char *buf;
"(type=%s, id=%llu) on device %s",
}
return (0);
}
/*
* Invoked when a userland contract holder approves (i.e. ACKs) a state change
*/
static int
{
}
/*
* Invoked when a userland contract holder blocks (i.e. NACKs) a state change
*/
static int
{
}
/*
* Creates a new contract synchronously with the breaking of an existing
* contract. Currently not supported.
*/
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*
* Core device contract implementation entry points
*/
contract_device_free, /* contop_free */
contract_device_abandon, /* contop_abandon */
contract_device_destroy, /* contop_destroy */
contract_device_status, /* contop_status */
contract_device_ack, /* contop_ack */
contract_device_nack, /* contop_nack */
contract_qack_notsup, /* contop_qack */
contract_device_newct /* contop_newct */
};
/*
* contract_device_init
*
* Initializes the device contract type.
*/
void
contract_device_init(void)
{
}
/*
* contract_device_create
*
* create a device contract given template "tmpl" and the "owner" process.
* May fail and return NULL if project.max-contracts would have been exceeded.
*
* Common device contract creation routine called for both open-time and
* non-open time device contract creation
*/
static cont_device_t *
{
char *minor;
char *path;
*errorp = 0;
"for device path (%s)", path);
return (NULL);
}
/*
* Lock out any parallel contract negotiations
*/
/*
* Only we hold a refernce to this contract. Safe to access
* the fields without a ct_lock
*/
/*
* It is safe to set the dip pointer in the contract
* as the contract will always be destroyed before the dip
* is released
*/
/*
* Since we are able to lookup the device, it is either
* online or degraded
*/
/*
* contract_ctor() initailizes the common portion of a contract
* contract_dtor() destroys the common portion of a contract
*/
/*
* contract_device_free() destroys the type specific
* portion of a contract and frees the contract.
* The "minor" path and "cred" is a part of the type specific
* portion of the contract and will be freed by
* contract_device_free()
*/
/* release barrier */
return (NULL);
}
/*
* Insert device contract into list hanging off the dip
* Bump up the ref-count on the contract to reflect this
*/
/* release barrier */
return (ctd);
}
/*
* Called when a device is successfully opened to create an open-time contract
* i.e. synchronously with a device open.
*/
int
{
char *path;
int error;
if (ctpp)
/*
* Check if we are in user-context i.e. if we have an lwp
*/
return (0);
}
return (0);
}
/*
* If the user set a minor path in the template before an open,
* ignore it. We use the minor path of the actual minor opened.
*/
"ignoring device minor path in active template: %s",
/*
* This is a copy of the actual activated template.
* Safe to make changes such as freeing the minor
* path in the template.
*/
}
"minor path from dev_t,spec {%lu, %d} for process (%d)",
return (1);
}
"create device contract for process (%d) holding "
"device (devt = %lu, spec_type = %d)",
return (1);
}
if (ctpp) {
}
return (0);
}
/*
* Called during contract negotiation by the device contract framework to wait
* for ACKs or NACKs from contract holders. If all responses are not received
* before a specified timeout, this routine times out.
*/
static uint_t
{
int timed_out = 0;
int ack;
char *f = "wait_for_acks";
/*
* some contract owner(s) didn't respond in time
*/
timed_out = 1;
}
ack = 0;
continue;
}
continue;
}
/* skip if non-negotiable contract */
if (ctd->cond_noneg) {
continue;
}
f, (void *)dip));
return (CT_NACK);
ack = 1;
f, (void *)dip));
}
}
if (ack) {
} else if (timed_out) {
f, (void *)dip));
} else {
f, (void *)dip));
}
return (result);
}
/*
* Determines the current state of a device (i.e a devinfo node
*/
static int
{
return (CT_DEV_EV_OFFLINE);
else if (DEVI_IS_DEVICE_DEGRADED(dip))
return (CT_DEV_EV_DEGRADED);
else
return (CT_DEV_EV_ONLINE);
}
/*
* Sets the current state of a device in a device contract
*/
static void
{
/* verify that barrier is held */
}
}
/*
* Core routine called by event-specific routines when an event occurs.
* Determines if an event should be be published, and if it is to be
* published, whether a negotiation should take place. Also implements
* NEGEND events which publish the final disposition of an event after
* negotiations are complete.
*
* When an event occurs on a minor node, this routine walks the list of
* contracts hanging off a devinfo node and for each contract on the affected
* dip, evaluates the following cases
*
* a. an event that is synchronous, breaks the contract and NONEG not set
* - bumps up the outstanding negotiation counts on the dip
* - marks the dip as undergoing negotiation (devi_ct_neg)
* - event of type CTE_NEG is published
* b. an event that is synchronous, breaks the contract and NONEG is set
* - sets the final result to CT_NACK, event is blocked
* - does not publish an event
* c. event is asynchronous and breaks the contract
* - publishes a critical event irrespect of whether the NONEG
* flag is set, since the contract will be broken and contract
* owner needs to be informed.
* d. No contract breakage but the owner has subscribed to the event
* - publishes the event irrespective of the NONEG event as the
* owner has explicitly subscribed to the event.
* e. NEGEND event
* - publishes a critical event. Should only be doing this if
* if NONEG is not set.
* f. all other events
* - Since a contract is not broken and this event has not been
* subscribed to, this event does not need to be published for
* for this contract.
*
* Once an event is published, what happens next depends on the type of
* event:
*
* a. NEGEND event
* - cleanup all state associated with the preceding negotiation
* and return CT_ACK to the caller of contract_device_publish()
* b. NACKed event
* - One or more contracts had the NONEG term, so the event was
* blocked. Return CT_NACK to the caller.
* c. Negotiated event
* - Call wait_for_acks() to wait for responses from contract
* holders. The end result is either CT_ACK (event is permitted),
* CT_NACK (event is blocked) or CT_NONE (no contract owner)
* responded. This result is returned back to the caller.
* d. All other events
* - If the event was asynchronous (i.e. not negotiated) or
* a contract was not broken return CT_ACK to the caller.
*/
static uint_t
{
int negend;
int match;
int sync = 0;
int broken = 0;
/* Is this a synchronous state change ? */
if (evtype != CT_EV_NEGEND) {
/* NOP if unsupported transition */
goto out;
}
goto out;
}
/*
* Negotiation end - set the state of the device in the contract
*/
if (evtype == CT_EV_NEGEND) {
}
/*
* If this device didn't go through negotiation, don't publish
* a NEGEND event - simply release the barrier to allow other
* device events in.
*/
negend = 0;
goto out;
} else if (evtype == CT_EV_NEGEND) {
/*
* There are negotiated contract breakages that
* need a NEGEND event
*/
negend = 1;
} else {
/*
* This is a new event, not a NEGEND event. Wait for previous
* contract events to complete.
*/
}
match = 0;
continue;
}
continue;
}
/* We have a matching contract */
match = 1;
ctid));
/*
* There are 4 possible cases
* 1. A contract is broken (dev not in acceptable state) and
* the state change is synchronous - start negotiation
* by sending a CTE_NEG critical event.
* 2. A contract is broken and the state change is
* asynchronous - just send a critical event and
* break the contract.
* 3. Contract is not broken, but consumer has subscribed
* to the event as a critical or informative event
* - just send the appropriate event
* 4. contract waiting for negend event - just send the critical
* NEGEND event.
*/
broken = 0;
broken = 1;
ctid));
}
/*
* Don't send event if
* - contract is not broken AND
* - contract holder has not subscribed to this event AND
* - contract not waiting for a NEGEND event
*/
"contract (%d): no publish reqd: event %d",
continue;
}
/*
* Note: need to kmem_zalloc() the event so mutexes are
* initialized automatically
*/
"ctid: %d", ctid));
if (ctd->cond_noneg) {
/* Nothing to publish. Event has been blocked */
"not publishing blocked ev: ctid: %d",
ctid));
continue;
}
ctid));
ctid));
CTE_INFO : 0;
ctd->cond_currev_id = 0;
ctd->cond_currev_type = 0;
ctd->cond_currev_ack = 0;
} else {
"ctid: %d, evtype: %d",
continue;
}
if (tnvl) {
if (negend) {
== 0);
&newct) == 0);
newct == 1 ? 0 :
"CTS_NEVID: %llu, CTS_NEWCT: %s",
}
}
}
/*
* by holding the dip's devi_ct_lock we ensure that
* publishing to all contracts.
*/
": %d", ctid));
} else if (negend) {
}
}
/*
* If "negend" set counter back to initial state (-1) so that
* other events can be published. Also clear the negotiation flag
* on dip.
*
* 0 .. n are used for counting.
* -1 indicates counter is available for use.
*/
if (negend) {
/*
* devi_ct_count not necessarily 0. We may have
* timed out in which case, count will be non-zero.
*/
(void *)dip));
} else {
/*
* for non-negotiated events or subscribed events or no
* matching contracts
*/
"dip=%p", (void *)dip));
/*
* only this function when called from contract_device_negend()
* can reset the counter to READY state i.e. -1. This function
* is so called for every event whether a NEGEND event is needed
* or not, but the negend event is only published if the event
* whose end they signal is a negotiated event for the contract.
*/
}
if (!match) {
/* No matching contracts */
/* a non-negotiable contract exists and this is a neg. event */
/* one or more contracts going through negotations */
} else {
/* no negotiated contracts or no broken contracts or NEGEND */
}
/*
* Release the lock only now so that the only point where we
* drop the lock is in wait_for_acks(). This is so that we don't
* miss cv_signal/cv_broadcast from contract holders
*/
out:
if (path)
return (result);
}
/*
* contract_device_offline
*
* Event publishing routine called by I/O framework when a device is offlined.
*/
{
/*
* If a contract offline is NACKED, the framework expects us to call
* NEGEND ourselves, since we know the final result
*/
}
return (result);
}
/*
* contract_device_degrade
*
* Event publishing routine called by I/O framework when a device
* moves to degrade state.
*/
/*ARGSUSED*/
void
{
}
/*
* contract_device_undegrade
*
* Event publishing routine called by I/O framework when a device
* moves from degraded state to online state.
*/
/*ARGSUSED*/
void
{
}
/*
* For all contracts which have undergone a negotiation (because the device
* moved out of the acceptable state for that contract and the state
* change is synchronous i.e. requires negotiation) this routine publishes
* a CT_EV_NEGEND event with the final disposition of the event.
*
* This event is always a critical event.
*/
void
{
(void *)dip));
}
/*
* Wrapper routine called by other subsystems (such as LDI) to start
* negotiations when a synchronous device state change occurs.
* Returns CT_ACK or CT_NACK.
*/
{
int result;
switch (evtype) {
case CT_DEV_EV_OFFLINE:
break;
default:
"not supported: event (%d) for dev_t (%lu) and spec (%d), "
break;
}
return (result);
}
/*
* A wrapper routine called by other subsystems (such as the LDI) to
* finalize event processing for a state change event. For synchronous
* state changes, this publishes NEGEND events. For asynchronous i.e.
* non-negotiable events this publishes the event.
*/
void
{
switch (evtype) {
case CT_DEV_EV_OFFLINE:
break;
case CT_DEV_EV_DEGRADED:
break;
case CT_DEV_EV_ONLINE:
break;
default:
"event (%d) for dev_t (%lu) and spec (%d), dip (%p)",
break;
}
}
/*
* Called by I/O framework when a devinfo node is freed to remove the
* association between a devinfo node and its contracts.
*/
void
{
/*
* Unlink the dip associated with this contract
*/
}
}
/*
* Barrier related routines
*/
static void
{
}
static void
{
}
static int
{
}
static int
{
}
static void
{
}
static void
{
}
}
static void
{
}
static int
{
return (-1);
}
}
return (0);
}