+ spin_unlock(&oig->oig_lock);
+
+ CDEBUG(D_CACHE, "oig %p completed, rc %d -> %d via %d, %d now "
+ "pending (racey)\n", oig, old_rc, oig->oig_rc, rc,
+ oig->oig_pending);
+ if (wake)
+ cfs_waitq_signal(wake);
+ oig_release(oig);
+}
+EXPORT_SYMBOL(oig_complete_one);
+
+static int oig_done(struct obd_io_group *oig)
+{
+ int rc = 0;
+ spin_lock(&oig->oig_lock);
+ if (oig->oig_pending <= 0)
+ rc = 1;
+ spin_unlock(&oig->oig_lock);
+ return rc;
+}
+
+static void interrupted_oig(void *data)
+{
+ struct obd_io_group *oig = data;
+ struct oig_callback_context *occ;
+
+ spin_lock(&oig->oig_lock);
+ /* We need to restart the processing each time we drop the lock, as
+ * it is possible other threads called oig_complete_one() to remove
+ * an entry elsewhere in the list while we dropped lock. We need to
+ * drop the lock because osc_ap_completion() calls oig_complete_one()
+ * which re-gets this lock ;-) as well as a lock ordering issue. */
+restart:
+ list_for_each_entry(occ, &oig->oig_occ_list, occ_oig_item) {
+ if (occ->interrupted)
+ continue;
+ occ->interrupted = 1;
+ spin_unlock(&oig->oig_lock);
+ occ->occ_interrupted(occ);
+ spin_lock(&oig->oig_lock);
+ goto restart;
+ }
+ spin_unlock(&oig->oig_lock);
+}
+
+int oig_wait(struct obd_io_group *oig)
+{
+ struct l_wait_info lwi = LWI_INTR(interrupted_oig, oig);
+ int rc;
+
+ CDEBUG(D_CACHE, "waiting for oig %p\n", oig);
+
+ do {
+ rc = l_wait_event(oig->oig_waitq, oig_done(oig), &lwi);
+ LASSERTF(rc == 0 || rc == -EINTR, "rc: %d\n", rc);
+ /* we can't continue until the oig has emptied and stopped
+ * referencing state that the caller will free upon return */
+ if (rc == -EINTR)
+ lwi = (struct l_wait_info){ 0, };
+ } while (rc == -EINTR);
+
+ LASSERTF(oig->oig_pending == 0,
+ "exiting oig_wait(oig = %p) with %d pending\n", oig,
+ oig->oig_pending);
+
+ CDEBUG(D_CACHE, "done waiting on oig %p rc %d\n", oig, oig->oig_rc);
+ return oig->oig_rc;
+}
+EXPORT_SYMBOL(oig_wait);
+
+void class_fail_export(struct obd_export *exp)
+{
+ int rc, already_failed;
+
+ spin_lock(&exp->exp_lock);
+ already_failed = exp->exp_failed;
+ exp->exp_failed = 1;
+ spin_unlock(&exp->exp_lock);
+
+ if (already_failed) {
+ CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n",
+ exp, exp->exp_client_uuid.uuid);
+ return;
+ }
+
+ CDEBUG(D_HA, "disconnecting export %p/%s\n",
+ exp, exp->exp_client_uuid.uuid);
+
+ if (obd_dump_on_timeout)
+ libcfs_debug_dumplog();
+
+ /* Most callers into obd_disconnect are removing their own reference
+ * (request, for example) in addition to the one from the hash table.
+ * We don't have such a reference here, so make one. */
+ class_export_get(exp);
+ rc = obd_disconnect(exp);
+ if (rc)
+ CERROR("disconnecting export %p failed: %d\n", exp, rc);
+ else
+ CDEBUG(D_HA, "disconnected export %p/%s\n",
+ exp, exp->exp_client_uuid.uuid);
+}
+EXPORT_SYMBOL(class_fail_export);
+
+char *obd_export_nid2str(struct obd_export *exp)
+{
+ if (exp->exp_connection != NULL)
+ return libcfs_nid2str(exp->exp_connection->c_peer.nid);
+
+ return "(no nid)";
+}
+EXPORT_SYMBOL(obd_export_nid2str);
+
+int obd_export_evict_by_nid(struct obd_device *obd, const char *nid)
+{
+ struct obd_export *doomed_exp = NULL;
+ int exports_evicted = 0;
+
+ lnet_nid_t nid_key = libcfs_str2nid((char *)nid);
+
+ do {
+ doomed_exp = lustre_hash_get_object_by_key(obd->obd_nid_hash_body,
+ &nid_key);
+ if (doomed_exp == NULL)
+ break;
+
+ LASSERTF(doomed_exp->exp_connection->c_peer.nid == nid_key,
+ "nid %s found, wanted nid %s, requested nid %s\n",
+ obd_export_nid2str(doomed_exp),
+ libcfs_nid2str(nid_key), nid);
+ LASSERTF(doomed_exp != obd->obd_self_export,
+ "self-export is hashed by NID?\n");
+ exports_evicted++;
+ CWARN("%s: evict NID '%s' (%s) #%d at adminstrative request\n",
+ obd->obd_name, nid, doomed_exp->exp_client_uuid.uuid,
+ exports_evicted);
+ class_fail_export(doomed_exp);
+ class_export_put(doomed_exp);
+ } while (1);
+
+ if (!exports_evicted)
+ CDEBUG(D_HA,"%s: can't disconnect NID '%s': no exports found\n",
+ obd->obd_name, nid);
+ return exports_evicted;
+}
+EXPORT_SYMBOL(obd_export_evict_by_nid);
+
+int obd_export_evict_by_uuid(struct obd_device *obd, const char *uuid)
+{
+ struct obd_export *doomed_exp = NULL;
+ struct obd_uuid doomed;
+ int exports_evicted = 0;
+
+ obd_str2uuid(&doomed, uuid);
+ if (obd_uuid_equals(&doomed, &obd->obd_uuid)) {
+ CERROR("%s: can't evict myself\n", obd->obd_name);
+ return exports_evicted;
+ }
+
+ doomed_exp = lustre_hash_get_object_by_key(obd->obd_uuid_hash_body,
+ &doomed);
+
+ if (doomed_exp == NULL) {
+ CERROR("%s: can't disconnect %s: no exports found\n",
+ obd->obd_name, uuid);
+ } else {
+ CWARN("%s: evicting %s at adminstrative request\n",
+ obd->obd_name, doomed_exp->exp_client_uuid.uuid);
+ class_fail_export(doomed_exp);
+ class_export_put(doomed_exp);
+ exports_evicted++;
+ }
+
+ return exports_evicted;
+}
+EXPORT_SYMBOL(obd_export_evict_by_uuid);
+
+/**
+ * kill zombie imports and exports
+ */
+void obd_zombie_impexp_cull(void)
+{
+ struct obd_import *import;
+ struct obd_export *export;
+ ENTRY;
+
+ do {
+ spin_lock (&obd_zombie_impexp_lock);
+
+ import = NULL;
+ if (!list_empty(&obd_zombie_imports)) {
+ import = list_entry(obd_zombie_imports.next,
+ struct obd_import,
+ imp_zombie_chain);
+ list_del(&import->imp_zombie_chain);