ldi_ev_register_callbacks - add a notify and/or finalize callback
#include <sys/sunldi.h>
int ldi_ev_register_callbacks(ldi_handle_t lh,
ldi_ev_cookie_t *cookie, ldi_ev_callback_t *callb,
void *arg, ldi_callback_id_t *id);
illumos DDI specific (illumos DDI)
ldi_handle_t lh
A layered handle representing the device for which the
event notification was requested.
ldi_ev_cookie_t *cookie
ldi_ev_callback_t *callb
A data structure which currently has the following
members:
struct ldi_ev_callback {
uint_t cb_vers;
int (*cb_notify)(ldi_handle_t,
ldi_ev_cookie_t cookie,
void *arg, void *ev_data);
void (*cb_finalize)(ldi_handle_t,
ldi_ev_cookie_t cookie,
int ldi_result,
void *arg,
void *ev_data);
} ldi_ev_callback_t;
where
cb_vers
Version of callback vector. Must be set to
LDI_EV_CB_VERS by the caller.
The arguments passed into the callbacks when they are invoked,
include:
int ldi_result
The actual result of the state change operation/event
passed to finalize callback: LDI_EV_SUCCESS: The state change succeeded
LDI_EV_FAILURE: The state change failed.
void *ev_data
Event specific data.
void *arg
A pointer to opaque caller private data.
ldi_callback_id_t *id
Unique system wide registration id returned by
ldi_ev_register_callbacks(9F) upon successful registration.
The ldi_ev_register_callbacks() interface allows layered drivers to
register notify and finalize callbacks for certain events. These events are
listed in the ldi_ev_get_cookie(9F) man page. The notify callback is
invoked only for events that can be blocked, just before the event occurs. The
notify event is not called for events serviced by the NDI event service
framework since such events are by definition asynchronous. Only the finalize
callback is invoked for such events. Layered drivers that have registered
notify callbacks for that event have the opportunity of blocking such events.
The finalize callback is invoked once the final disposition of the state of a
device (specifically a device minor node) is known. The callback is invoked
with this result, either LDI_EV_SUCCESS (state change succeeded) or
LDI_EV_FAILURE (state change failed). This allows layered driver
consumers to finalize any changes they made in response to a previous
"notify" callback.
For example, a layered driver's notify callback may be invoked in
response to a LDI_EV_OFFLINE event. The layered driver may
reconfigure itself to stop using the device and permit the change to go
forward. Once that happens, the I/O framework attempts to actually
take the device offline. This offline attempt can have two possible
outcomes: success or failure. In the former case, the finalize callback is
invoked with the ldi_result argument set to LDI_EV_SUCCESS and
the layered driver knows that the device has been taken offline. In the
latter case, finalize is invoked with the ldi_result set to
LDI_EV_FAILURE and the layered driver knows that the state change
failed. In this case, it may choose to reconfigure itself to start using the
device again.
Finalize callbacks can be registered for all events including
events that cannot be blocked.
A layered driver can also propagate these events up the software
stack by using interfaces offered by the LDI event framework. The
layered driver may use ldi_ev_notify() to propagate notify events
occurring on minors it imports onto minors it exports. Similarly, it may use
ldi_ev_finalize() to propagate finalize events. Both
ldi_ev_notify() and ldi_ev_finalize() propagate events to
device contracts as well as LDI callbacks registered against the exported
minor nodes.
The LDI event framework has the following guarantees and
requirements with respect to these callbacks:
- 1.
- The notify() callback is invoked before an event (represented by
the event cookie) occurs on a device (represented by the layered driver
handle) and is invoked only for events that can be blocked. If the
callback returns LDI_EV_FAILURE, the event is blocked. If the
callback returns LDI_EV_SUCCESS, the event is allowed to proceed.
If any other value is returned, it is an error. An error message is logged
and the event is blocked. An example of an event that can be blocked and
for which notify callbacks may be invoked is the offline event
LDI_EV_OFFLINE.
- 2.
- The finalize callback is invoked for all events (including events that
cannot be blocked) after the event has occurred. It is invoked with either
LDI_EV_SUCCESS indicating that the event successfully happened or
LDI_EV_FAILURE indicating that the event did not occur. The
finalize callback returns no values. Good examples of events that cannot
be blocked are the degrade event (LDI_EV_DEGRADE) and events
serviced by the NDI event service framework.
- 3.
- Layered drivers may register one or both of these callbacks (that is, only
for a notify event or only for a finalize event or for both) against any
LDI handle that they may possess. If a finalize or notify event is
not being registered, the corresponding pointer in the
ldi_ev_callback_t structure must be set to NULL. It is an
error to attempt a registration with both callbacks set to
NULL.
- 4.
- A notify and/or finalize callback is invoked only if the corresponding
LDI handle is open. If an LDI handle against which the
callbacks are registered is closed, the corresponding finalize and notify
callbacks is not invoked as it is assumed that the layered driver is no
longer interested in the device. See number 5 below for the exception to
this rule.
- 5.
- A layered driver that closes it's LDI handle in it's notify routine
receives the corresponding finalize callback after the event has occurred.
Because the LDI handle has been closed, the finalize callback is
invoked with a NULL LDI handle. It is the responsibility of
the layered driver to maintain state in it's private
"arg" parameter so that it can reopen the device (if
desired) in it's finalize callback.
One example where this may happen is with the
LDI_EV_OFFLINE event. A layered driver's notify callback may be
invoked for an offline event. The layered driver may choose to allow
this event to proceed. In that case, since it has a layered open of the
device, it must close the LDI handle so that the offline event
can succeed (an offline of a device does not succeed if there is any
open of the device, layered or otherwise). Since the layered driver has
closed the LDI handle in the notify routine, it's finalize
callback (if any) is invoked with a NULL LDI handle. It is
the responsibility of the layered driver to maintain state (such as the
device path or devid) in it's private "arg"
parameter, so that in the finalize routine, it can do a layered open of
the device if the device offline failed.
This is the only exception where the finalize callback is
invoked if the LDI handle has been closed. In all other cases if
the LDI handle has been closed, no corresponding callbacks is
invoked.
- 6.
- In order for the offline event to succeed (LDI_EV_OFFLINE), it is
imperative that there be no opens (including LDI handles) to the
device. If a layered driver's notify callback is invoked for an offline
event and the driver intends to allow the offline to proceed, the driver
must close the corresponding LDI handle.
- 7.
- The notify and finalize callbacks are not automatically unregistered even
if the corresponding LDI handle has been closed. It is the
responsibility of the layered driver to unregister these callbacks when
they are not required. It may do so using the
ldi_ev_remove_callbacks(9F) interface. The LDI framework may
panic if the entity registering the callback (such as a dip,
dev_t or module) no longer exists on the system and the
corresponding callbacks have not been unregistered.
- 8.
- The LDI event framework guarantees that if a layered driver
receives a notify event, it also receives a finalize event except if the
layered consumer itself blocked the event (that is, it returned
LDI_EV_FAILURE from it's notify callback. In this case, the layered
driver knows that the event has been blocked and therefore does not need
the finalize callback.
- 9.
- If a layered driver propagates notify events on minors it imports to
minors it exports, it must first propagate these events up the software
stack via ldi_eve_notify() in it's notify callback. It must do so
before attempting to check if it blocks the event. This is required,
because a layered driver cannot release the device if consumers up the
stack are still using the device. If ldi_ev_notify() returns
LDI_EV_FAILURE, the callback must immediately return
LDI_EV_FAILURE from it's notify callback. If ldi_ev_notify()
returns LDI_EV_SUCCESS, then the state change is permissible as far
as consumers higher up in the software stack are concerned. The layered
driver must then determine if it can permit the state change. If the state
change is to be allowed, the layered driver must return
LDI_EV_SUCCESS. If the layered driver determines that the state
change should not be permitted, it must invoke ldi_ev_finalize() on
minors it exports with a result of LDI_EV_FAILURE (to inform
consumers up the stack) and then return LDI_EV_FAILURE from it's
notify callback.
- 10.
- The LDI event framework generates finalize events at the earliest
point where a failure is detected. If the failure is detected in the
framework (such as in ldi_ev_notify()) the framework generates the
finalize events. In the event that a failure is first detected in a
layered driver (that is, in the notify callback of a layered driver) the
layered driver must use ldi_ev_finalize() to send finalize events
up the software stack . See the examples for code snippets describing this
scenario.
- 11.
- The finalize callback must first reconfigure itself before attempting to
propagate the event up the software stack via ldi_ev_finalize(9F).
This is so that the minors it exports are available and ready for use
before the finalize event is propagated up the software stack.
- 12.
- It may so happen that the event propagated up the software stack is not
the same as the event for which a layered driver's notify/finalize
callback is invoked. For example, a layered driver's callback(s) may be
invoked for an offline event, but the driver may choose to only propagate
the degraded event to its consumers (since it may have a mirror/copy of
the data on the device.) In that case, the layered driver must generate a
different event cookie (that is, one corresponding to the degraded event
via ldi_ev_get_cookie(9F)) and use that cookie in its propagation
calls (that is, ldi_ev_notify(9F) and
ldi_ev_finalize(9F)).
Once the registration of the callback(s) is successful, an opaque
ldi_callback_id_t structure is returned which may be used to
unregister the callback(s) later.
The return values for this function are:
LDI_EV_SUCCESS
Callback(s) added successfully.
LDI_EV_FAILURE
Failed to add callback(s).
This function can be called from user and kernel contexts only.
Example 1 Registration and Callbacks for the OFFLINE Event
The following example shows how the
ldi_ev_register_callbacks() function performs a registration and
callback for the offline event:
static int
event_register(void)
{
ldi_handle_t lh;
ldi_ev_callback_t callb;
ldi_ev_cookie_t off_cookie;
if (ldi_ev_get_cookie(lh, LDI_EV_OFFLINE, &off_cookie)
== LDI_EV_FAILURE)
goto fail;
callb.cb_vers = LDI_EV_CB_VERS;
callb.cb_notify = off_notify;
callb.cb_finalize = off_finalize;
if (ldi_ev_register_callbacks(lh, off_cookie, &callb, arg, &id)
!= LDI_EV_SUCCESS)
goto fail;
}
static void
event_unregister(ldi_callback_id_t id)
{
ldi_ev_remove_callbacks(id);
}
static int
off_notify(ldi_handle_t lh, ldi_ev_cookie_t off_cookie, void *arg,
void *ev_data)
{
ASSERT(strcmp(ldi_ev_get_type(off_cookie), LDI_EV_OFFLINE) == 0);
/* Map imported minors to exported minor */
widget_map(lh, &minor, &spec_type);
/*
* Call ldi_ev_notify() to propagate events to our consumers.
* This *must* happen before we check if offline should be blocked
*/
if (ldi_ev_notify(dip, minor, spec_type, off_cookie, ev_data)
!= LDI_EV_SUCCESS)
return (LDI_EV_FAILURE);
/*
* Next, check if we can allow the offline
*/
if (widget_check(lh) == WIDGET_SUCCESS) {
widget_save_path(arg, lh);
widget_reconfigure(lh, RELEASE);
ldi_close(lh);
return (LDI_EV_SUCCESS)
}
/*
* We cannot permit the offline. The first layer that detects
* failure i.e. us, must generate finalize events for our
consumers
*/
ldi_ev_finalize(dip, minor, spec_type, LDI_EV_FAILURE, off_cookie,
ev_data);
return (LDI_EV_FAILURE);
}
/*
/*
* The finalize callback will only be called if we returned LDI_EV_SUCCESS
* in our notify callback. ldi_result passed in may be SUCCESS or FAILURE
*/
static void
off_finalize(ldi_handle_t NULL_lh, ldi_ev_cookie_t off_cookie,
int ldi_result, void *arg, void *ev_data)
{
ldi_handle_t lh;
ASSERT(strcmp(ldi_ev_get_type(off_cookie), LDI_EV_OFFLINE) == 0);
path = widget_get_path(arg);
widget_map_by_path(path, &minor, &spec_type);
if (ldi_result == LDI_EV_SUCCESS) {
ldi_ev_finalize(dip, minor, spec_type, LDI_EV_SUCCESS,
off_cookie, ev_data);
return;
}
/* The offline failed. Reopen the device */
ldi_open_by_name(path, &lh);
widget_reconfigure(lh, REACQUIRE);
ldi_ev_finalize(dip, minor, spec_type, LDI_EV_FAILURE, off_cookie,
ev_data);
}
Example 2 Registration and Callbacks for the DEGRADE
Event
The following example shows how the
ldi_ev_register_callbacks() function performs a registration and
callback for the degrade event:
static int
event_register(void)
{
ldi_handle_t lh;
ldi_ev_callback_t callb;
ldi_ev_cookie_t dgrd_cookie;
if (ldi_ev_get_cookie(lh, LDI_EV_DEGRADE, &dgrd_cookie)
== LDI_EV_FAILURE)
goto fail;
/* no notify callbacks allowed for degrade events */
callb.cb_vers = LDI_EV_CB_VERS;
callb.cb_notify = NULL; /* NULL, notify cannot be used for
DEGRADE */
callb.cb_finalize = dgrd_finalize;
if (ldi_ev_register_callbacks(lh, dgrd_cookie, &callb, arg, &id)
!= LDI_EV_SUCCESS)
goto fail;
}
static void
event_unregister(ldi_callback_id_t id)
{
ldi_ev_remove_callbacks(id);
}
/*
* For degrade events. ldi_result will always be LDI_EV_SUCCESS
*/
static void
dgrd_finalize(ldi_handle_t lh, ldi_ev_cookie_t off_cookie,
int ldi_result, void *arg, void *ev_data)
{
ASSERT(ldi_result == LDI_EV_SUCCESS);
ASSERT(strcmp(ldi_ev_get_type(off_cookie), LDI_EV_DEGRADE) == 0);
widget_map(lh, &minor, &spec_type);
widget_reconfigure(lh, RELEASE);
ldi_ev_finalize(dip, minor, spec_type, LDI_EV_SUCCESS, d
grd_cookie, ev_data);
}