+static void ptlrpc_reset_reqs_generation(struct obd_import *imp)
+{
+ struct ptlrpc_request *old, *tmp;
+
+ /* tag all resendable requests generated before disconnection
+ * notice this code is part of disconnect-at-idle path only */
+ list_for_each_entry_safe(old, tmp, &imp->imp_delayed_list,
+ rq_list) {
+ spin_lock(&old->rq_lock);
+ if (old->rq_import_generation == imp->imp_generation - 1 &&
+ ((imp->imp_initiated_at == imp->imp_generation) ||
+ !old->rq_no_resend))
+ old->rq_import_generation = imp->imp_generation;
+ spin_unlock(&old->rq_lock);
+ }
+}
+
+static int ptlrpc_disconnect_idle_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *args, int rc)
+{
+ struct obd_import *imp = req->rq_import;
+ int connect = 0;
+
+ DEBUG_REQ(D_HA, req, "inflight=%d, refcount=%d: rc = %d",
+ atomic_read(&imp->imp_inflight),
+ atomic_read(&imp->imp_refcount), rc);
+
+ spin_lock(&imp->imp_lock);
+ /* DISCONNECT reply can be late and another connection can just
+ * be initiated. so we have to abort disconnection. */
+ if (req->rq_import_generation == imp->imp_generation &&
+ imp->imp_state != LUSTRE_IMP_CLOSED) {
+ LASSERTF(imp->imp_state == LUSTRE_IMP_CONNECTING,
+ "%s\n", ptlrpc_import_state_name(imp->imp_state));
+ imp->imp_state = LUSTRE_IMP_IDLE;
+ memset(&imp->imp_remote_handle, 0,
+ sizeof(imp->imp_remote_handle));
+ /* take our DISCONNECT into account */
+ if (atomic_read(&imp->imp_inflight) > 1) {
+ imp->imp_generation++;
+ imp->imp_initiated_at = imp->imp_generation;
+ import_set_state_nolock(imp, LUSTRE_IMP_NEW);
+ ptlrpc_reset_reqs_generation(imp);
+ connect = 1;
+ }
+ }
+
+ if (connect) {
+ rc = ptlrpc_connect_import_locked(imp);
+ if (rc >= 0)
+ ptlrpc_pinger_add_import(imp);
+ } else {
+ spin_unlock(&imp->imp_lock);
+ }
+
+ return 0;
+}
+
+int ptlrpc_disconnect_and_idle_import(struct obd_import *imp)
+{
+ struct ptlrpc_request *req;
+ ENTRY;
+
+ if (imp->imp_obd->obd_force)
+ RETURN(0);
+
+ if (ptlrpc_import_in_recovery(imp))
+ RETURN(0);
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state != LUSTRE_IMP_FULL) {
+ spin_unlock(&imp->imp_lock);
+ RETURN(0);
+ }
+ spin_unlock(&imp->imp_lock);
+
+ req = ptlrpc_disconnect_prep_req(imp);
+ if (IS_ERR(req))
+ RETURN(PTR_ERR(req));
+
+ CDEBUG_LIMIT(imp->imp_idle_debug, "%s: disconnect after %llus idle\n",
+ imp->imp_obd->obd_name,
+ ktime_get_real_seconds() - imp->imp_last_reply_time);
+
+ /* don't make noise at reconnection */
+ spin_lock(&imp->imp_lock);
+ imp->imp_was_idle = 1;
+ spin_unlock(&imp->imp_lock);
+
+ req->rq_interpret_reply = ptlrpc_disconnect_idle_interpret;
+ ptlrpcd_add_req(req);
+
+ RETURN(0);
+}
+EXPORT_SYMBOL(ptlrpc_disconnect_and_idle_import);
+