LDI_EV_REGISTER_CALLBACKS(9F) | Kernel Functions for Drivers | LDI_EV_REGISTER_CALLBACKS(9F) |
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
ldi_ev_cookie_t *cookie
ldi_ev_callback_t *callb
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
The arguments passed into the callbacks when they are invoked, include:
int ldi_result
void *ev_data
void *arg
ldi_callback_id_t *id
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:
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, its 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 its 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.
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
LDI_EV_FAILURE
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); }
ldi_ev_finalize(9F), ldi_ev_get_cookie(9F), ldi_ev_notify(9F), ldi_ev_remove_callbacks(9F)
August 22, 2023 | OmniOS |