Index: 1.8.6/lnet/include/lnet/lib-lnet.h =================================================================== --- 1.8.6/lnet/include/lnet/lib-lnet.h (revision 26994) +++ 1.8.6/lnet/include/lnet/lib-lnet.h (revision 26995) @@ -753,6 +753,20 @@ void lnet_register_lnd(lnd_t *lnd); void lnet_unregister_lnd(lnd_t *lnd); int lnet_set_ip_niaddr (lnet_ni_t *ni); +/* + * Register a function that will be called each time an lnd reports that + * a directly-connected peer has transitioned down or up. + * Redundant register attempts will result in -EEXIST. + */ +int LNetRegisterNotifyCallback(lnet_notify_callback cb); + +/* + * Deregister a function that was successfully registered via + * lnet_register_notify_callback(). Attempts to deregister an unknown + * function will result in -ENOENT. + */ +int LNetUnregisterNotifyCallback(lnet_notify_callback cb); + #ifdef __KERNEL__ int lnet_connect(cfs_socket_t **sockp, lnet_nid_t peer_nid, __u32 local_ip, __u32 peer_ip, int peer_port); Index: 1.8.6/lnet/include/lnet/lib-types.h =================================================================== --- 1.8.6/lnet/include/lnet/lib-types.h (revision 26994) +++ 1.8.6/lnet/include/lnet/lib-types.h (revision 26995) @@ -572,6 +572,8 @@ typedef struct cfs_waitq_t ln_waitq; struct semaphore ln_api_mutex; struct semaphore ln_lnd_mutex; + + struct list_head ln_notify_cbs; /* notify callbacks */ #else # ifndef HAVE_LIBPTHREAD int ln_lock; @@ -665,4 +667,25 @@ typedef struct #endif } lnet_t; +/* Function that will be called each time an lnd reports (by calling lnd_notify()) + * that its connection to a peer has gone up or down. + * + * Arguments are identical to those of lnet_notify(): + * + * ni Identifies LNET instance. + * nid Identifies the peer. + * alive Non-zero if the connection to the peer has come up, + * zero if the connection has gone down. + * when Timestamp of the transition, in jiffies. + * + * LNET must not be reentered via this function--deadlock will occur, + * since the LNET global lock is held over the callback. */ +typedef void (*lnet_notify_callback)(struct lnet_ni *ni, lnet_nid_t nid, int alive, cfs_time_t when); + +/* List entries to remember callbacks registered by upper layers. */ +typedef struct lnet_notify_entry { + struct list_head ln_list; /* list of all callbacks */ + lnet_notify_callback ln_cb; /* the callback */ +} lnet_notify_entry_t; + #endif Index: 1.8.6/lnet/lnet/api-ni.c =================================================================== --- 1.8.6/lnet/lnet/api-ni.c (revision 26994) +++ 1.8.6/lnet/lnet/api-ni.c (revision 26995) @@ -1223,6 +1223,8 @@ LNetInit(void) CFS_INIT_LIST_HEAD(&the_lnet.ln_zombie_rcd); #ifdef __KERNEL__ + CFS_INIT_LIST_HEAD(&the_lnet.ln_notify_cbs); + /* All LNDs apart from the LOLND are in separate modules. They * register themselves when their module loads, and unregister * themselves when their module is unloaded. */ @@ -1825,3 +1827,72 @@ lnet_ping (lnet_process_id_t id, int tim LIBCFS_FREE(info, infosz); return rc; } + +#ifdef __KERNEL__ + +/* Register a function that will be called each time an lnd reports that + * a directly-connected peer has transitioned down or up. + * Redundant register attempts will result in -EEXIST. */ +int LNetRegisterNotifyCallback(lnet_notify_callback cb) +{ + int rc = 0; + lnet_notify_entry_t *entry, *new_entry; + + LASSERT (the_lnet.ln_init); + LASSERT (cb != NULL); + + LIBCFS_ALLOC(new_entry, sizeof(*new_entry)); + if (new_entry == NULL) + return -ENOMEM; + + LNET_LOCK(); + + list_for_each_entry (entry, &the_lnet.ln_notify_cbs, ln_list) { + if (entry->ln_cb == cb) { + rc = -EEXIST; + goto error_exit; + } + } + + CFS_INIT_LIST_HEAD(&new_entry->ln_list); + new_entry->ln_cb = cb; + list_add_tail(&new_entry->ln_list, &the_lnet.ln_notify_cbs); + + error_exit: LNET_UNLOCK(); + return rc; +} + +/* Deregister a function that was successfully registered via + * lnet_register_notify_callback(). Attempts to deregister an unknown + * function will result in -ENOENT. */ +int LNetUnregisterNotifyCallback(lnet_notify_callback cb) +{ + lnet_notify_entry_t *entry; + + LASSERT (the_lnet.ln_init); + LNET_LOCK(); + + list_for_each_entry (entry, &the_lnet.ln_notify_cbs, ln_list) { + if (entry->ln_cb == cb) { + list_del (&entry->ln_list); + LNET_UNLOCK(); + LIBCFS_FREE(entry, sizeof(*entry)); + return 0; + } + } + + LNET_UNLOCK(); + return -ENOENT; +} + +#else +int LNetRegisterNotifyCallback(lnet_notify_callback cb) +{ + return -EOPNOTSUPP; +} + +int LNetUnregisterNotifyCallback(lnet_notify_callback cb) +{ + return -EOPNOTSUPP; +} +#endif Index: 1.8.6/lnet/lnet/router.c =================================================================== --- 1.8.6/lnet/lnet/router.c (revision 26994) +++ 1.8.6/lnet/lnet/router.c (revision 26995) @@ -99,6 +99,17 @@ lnet_peers_start_down(void) return check_routers_before_use; } +/* Notify the upper layer of a change in lnd status. + * Must be called with LNET_LOCK held. */ +static void lnet_do_ul_notify_locked(lnet_peer_t *lp) +{ + lnet_notify_entry_t *entry; + + list_for_each_entry (entry, &the_lnet.ln_notify_cbs, ln_list) { + (entry->ln_cb)(lp->lp_ni, lp->lp_nid, lp->lp_alive, lp->lp_last_alive); + } +} + void lnet_notify_locked(lnet_peer_t *lp, int notifylnd, int alive, cfs_time_t when) { @@ -123,6 +134,9 @@ lnet_notify_locked(lnet_peer_t *lp, int lp->lp_notify = 1; lp->lp_notifylnd |= notifylnd; + /* Notify upper layer */ + lnet_do_ul_notify_locked(lp); + CDEBUG(D_NET, "set %s %d\n", libcfs_nid2str(lp->lp_nid), alive); }