Looks there is a race can cause export reference leak on difficult reply (and when rs_no_ack enabled):
MDT umount calls server_disconnect_export() to disconnects it's export, server_disconnect_exports() tries to complete all it's outstanding (difficult) replies, that'll call into ptlrpc_handle_rs() at the end, where the reply sate is removed from uncommited list, and LNetMDUnlink() is called to trigger an unlink event, reply_out_callback() catches the event and try to finialize the reply sate (where the export hold by reply state will be released) by following code:
spin_lock(&svcpt->scp_rep_lock);
spin_lock(&rs->rs_lock);
rs->rs_on_net = 0;
if (!rs->rs_no_ack ||
rs->rs_transno <=
rs->rs_export->exp_obd->obd_last_committed)
ptlrpc_schedule_difficult_reply(rs);
spin_unlock(&rs->rs_lock);
spin_unlock(&svcpt->scp_rep_lock);
We can see if rs_no_ack is false (COS not enabled), or the transaction has been committed, the reply state is finalized here, otherwise, it relies on commit callback, however, for the export already been disconnected by umount or whatever reason, the reply state is already being removed from the uncommitted list, so nobody will execute the reply state (until unload ptlrpc module). I think adding one more check here to see if the reply state is still on uncommitted list is necessary.
COS is usually not enabled, so this defect won't be triggered easily, however by the change of LU-3538, rs_no_ack is set unpurposely no matter if COS is enabled:
@@ -2530,24 +2589,27 @@ static void mdt_save_lock(struct mdt_thread_info *info, struct lustre_handle *h,
struct mdt_device *mdt = info->mti_mdt;
struct ldlm_lock *lock = ldlm_handle2lock(h);
struct ptlrpc_request *req = mdt_info_req(info);
- int no_ack = 0;
+ int cos;
+
+ cos = (mdt_cos_is_enabled(mdt) ||
+ mdt_slc_is_enabled(mdt));
LASSERTF(lock != NULL, "no lock for cookie "LPX64"\n",
h->cookie);
+
/* there is no request if mdt_object_unlock() is called
* from mdt_export_cleanup()->mdt_add_dirty_flag() */
if (likely(req != NULL)) {
CDEBUG(D_HA, "request = %p reply state = %p"
" transno = "LPD64"\n", req,
req->rq_reply_state, req->rq_transno);
- if (mdt_cos_is_enabled(mdt)) {
- no_ack = 1;
+ if (cos) {
ldlm_lock_downgrade(lock, LCK_COS);
mode = LCK_COS;
}
- ptlrpc_save_lock(req, h, mode, no_ack);
+ ptlrpc_save_lock(req, h, mode, cos);
} else {
- ldlm_lock_decref(h, mode);
+ mdt_fid_unlock(h, mode);
}
That could be the reason why it can be triggered more easily today.
Landed for 2.9