LDI_EV_REGISTER_CALLBACKS(9F)LDI_EV_REGISTER_CALLBACKS(9F)NAMEldi_ev_register_callbacks - add a notify and/or finalize callback
SYNOPSIS
#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);
INTERFACE LEVEL
Solaris DDI specific (Solaris DDI)
PARAMETERS
ldi_handle_t lh
A layered handle representing the device for which the event noti‐
fication was requested.
ldi_ev_cookie_t *cookie
An opaque event cookie for the event type returned by a previous
call to ldi_ev_get_cookie(9F).
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 opera‐
tion/event passed to finalize callback: LDI_EV_SUC‐
CESS: 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_regis‐
ter_callbacks(9F) upon successful registration.
DESCRIPTION
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 asynchro‐
nous. 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 lay‐
ered 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 (repre‐
sented 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_SUC‐
CESS, 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 pos‐
sess. 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 registra‐
tion 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 corre‐
sponding 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 call‐
back.
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 oth‐
erwise). 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 han‐
dles) 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 cor‐
responding 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 call‐
back. 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 con‐
cerned. 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 mir‐
ror/copy of the data on the device.) In that case, the lay‐
ered 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 propaga‐
tion calls (that is, ldi_ev_notify(9F) and ldi_ev_final‐
ize(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.
RETURN VALUES
The return values for this function are:
LDI_EV_SUCCESS
Callback(s) added successfully.
LDI_EV_FAILURE
Failed to add callback(s).
CONTEXT
This function can be called from user and kernel contexts only.
EXAMPLES
Example 1 Registration and Callbacks for the OFFLINE Event
The following example shows how the ldi_ev_register_callbacks() func‐
tion 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() func‐
tion 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);
}
SEE ALSOldi_ev_finalize(9F), ldi_ev_get_cookie(9F), ldi_ev_notify(9F),
ldi_ev_remove_callbacks(9F)
Oct 24, 2012 LDI_EV_REGISTER_CALLBACKS(9F)