+ ni->ni_lnd->lnd_refcount--;
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ islo = ni->ni_lnd->lnd_type == LOLND;
+
+ LASSERT(!in_interrupt());
+ (ni->ni_lnd->lnd_shutdown)(ni);
+
+ /* can't deref lnd anymore now; it might have unregistered
+ * itself... */
+
+ if (!islo)
+ CDEBUG(D_LNI, "Removed LNI %s\n",
+ libcfs_nid2str(ni->ni_nid));
+
+ lnet_ni_free(ni);
+ i = 2;
+ lnet_net_lock(LNET_LOCK_EX);
+ }
+}
+
+static void
+lnet_shutdown_lndnis(void)
+{
+ int i;
+ lnet_ni_t *ni;
+
+ /* NB called holding the global mutex */
+
+ /* All quiet on the API front */
+ LASSERT(!the_lnet.ln_shutdown);
+ LASSERT(the_lnet.ln_refcount == 0);
+ LASSERT(list_empty(&the_lnet.ln_nis_zombie));
+
+ lnet_net_lock(LNET_LOCK_EX);
+ the_lnet.ln_shutdown = 1; /* flag shutdown */
+
+ /* Unlink NIs from the global table */
+ while (!list_empty(&the_lnet.ln_nis)) {
+ ni = list_entry(the_lnet.ln_nis.next,
+ lnet_ni_t, ni_list);
+ /* move it to zombie list and nobody can find it anymore */
+ list_move(&ni->ni_list, &the_lnet.ln_nis_zombie);
+ lnet_ni_decref_locked(ni, 0); /* drop ln_nis' ref */
+
+ if (!list_empty(&ni->ni_cptlist)) {
+ list_del_init(&ni->ni_cptlist);
+ lnet_ni_decref_locked(ni, 0);
+ }
+ }
+
+ /* Drop the cached eqwait NI. */
+ if (the_lnet.ln_eq_waitni != NULL) {
+ lnet_ni_decref_locked(the_lnet.ln_eq_waitni, 0);
+ the_lnet.ln_eq_waitni = NULL;
+ }
+
+ /* Drop the cached loopback NI. */
+ if (the_lnet.ln_loni != NULL) {
+ lnet_ni_decref_locked(the_lnet.ln_loni, 0);
+ the_lnet.ln_loni = NULL;
+ }
+
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ /* Clear lazy portals and drop delayed messages which hold refs
+ * on their lnet_msg_t::msg_rxpeer */
+ for (i = 0; i < the_lnet.ln_nportals; i++)
+ LNetClearLazyPortal(i);
+
+ /* Clear the peer table and wait for all peers to go (they hold refs on
+ * their NIs) */
+ lnet_peer_tables_cleanup(NULL);
+
+ lnet_net_lock(LNET_LOCK_EX);
+
+ lnet_clear_zombies_nis_locked();
+ the_lnet.ln_shutdown = 0;
+ lnet_net_unlock(LNET_LOCK_EX);
+}
+
+int
+lnet_shutdown_lndni(__u32 net)
+{
+ lnet_ping_info_t *pinfo;
+ lnet_handle_md_t md_handle;
+ lnet_ni_t *found_ni = NULL;
+ int ni_count;
+ int rc;
+
+ if (LNET_NETTYP(net) == LOLND)
+ return -EINVAL;
+
+ ni_count = lnet_get_ni_count();
+
+ /* create and link a new ping info, before removing the old one */
+ rc = lnet_ping_info_setup(&pinfo, &md_handle, ni_count - 1, false);
+ if (rc != 0)
+ return rc;
+
+ /* proceed with shutting down the NI */
+ lnet_net_lock(LNET_LOCK_EX);
+
+ found_ni = lnet_net2ni_locked(net, 0);
+ if (found_ni == NULL) {
+ lnet_net_unlock(LNET_LOCK_EX);
+ lnet_ping_md_unlink(pinfo, &md_handle);
+ lnet_ping_info_free(pinfo);
+ return -EINVAL;
+ }
+
+ /* decrement the reference counter on found_ni which was
+ * incremented when we called lnet_net2ni_locked() */
+ lnet_ni_decref_locked(found_ni, 0);
+
+ /* Move ni to zombie list so nobody can find it anymore */
+ list_move(&found_ni->ni_list, &the_lnet.ln_nis_zombie);
+
+ /* Drop the lock reference for the ln_nis ref. */
+ lnet_ni_decref_locked(found_ni, 0);
+
+ if (!list_empty(&found_ni->ni_cptlist)) {
+ list_del_init(&found_ni->ni_cptlist);
+ lnet_ni_decref_locked(found_ni, 0);
+ }
+
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ /* Do peer table cleanup for this ni */
+ lnet_peer_tables_cleanup(found_ni);
+
+ lnet_net_lock(LNET_LOCK_EX);
+ lnet_clear_zombies_nis_locked();
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ lnet_ping_target_update(pinfo, md_handle);
+
+ return 0;
+}
+
+static int
+lnet_startup_lndnis(struct list_head *nilist, __s32 peer_timeout,
+ __s32 peer_cr, __s32 peer_buf_cr, __s32 credits,
+ int *ni_count)
+{
+ int rc = 0;
+ struct lnet_ni *ni;
+ int lnd_type;
+ lnd_t *lnd;
+ struct lnet_tx_queue *tq;
+ int i;
+
+ while (!list_empty(nilist)) {
+ ni = list_entry(nilist->next, lnet_ni_t, ni_list);
+ lnd_type = LNET_NETTYP(LNET_NIDNET(ni->ni_nid));
+
+ if (!libcfs_isknown_lnd(lnd_type))
+ goto failed;
+
+ if (lnd_type == CIBLND ||
+ lnd_type == OPENIBLND ||
+ lnd_type == IIBLND ||
+ lnd_type == VIBLND) {
+ CERROR("LND %s obsoleted\n",
+ libcfs_lnd2str(lnd_type));
+ goto failed;
+ }
+
+ /* Make sure this new NI is unique. */
+ lnet_net_lock(LNET_LOCK_EX);
+ if (!lnet_net_unique(LNET_NIDNET(ni->ni_nid),
+ &the_lnet.ln_nis)) {
+ if (lnd_type == LOLND) {
+ lnet_net_unlock(LNET_LOCK_EX);
+ list_del(&ni->ni_list);
+ lnet_ni_free(ni);
+ continue;
+ }
+
+ CERROR("Net %s is not unique\n",
+ libcfs_net2str(LNET_NIDNET(ni->ni_nid)));
+ lnet_net_unlock(LNET_LOCK_EX);
+ goto failed;
+ }
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ LNET_MUTEX_LOCK(&the_lnet.ln_lnd_mutex);
+ lnd = lnet_find_lnd_by_type(lnd_type);