LDI_EV_REGISTER_CALLBACKS(9F)                   Kernel Functions for Drivers
NAME
       ldi_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
       illumos DDI specific (illumos DDI)
PARAMETERS
       ldi_handle_t lh           A layered handle representing the device for which the event
           notification 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
                          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.
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 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 its 
LDI handle in its 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 its private "
arg" parameter so that it
                  can reopen the device (if desired) in its 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,
                  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.
           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 its 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
                  its 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 its 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 its 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.
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()       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);
         }
SEE ALSO
       ldi_ev_finalize(9F), 
ldi_ev_get_cookie(9F), 
ldi_ev_notify(9F),       
ldi_ev_remove_callbacks(9F)                               August 22, 2023 LDI_EV_REGISTER_CALLBACKS(9F)