[RFC PATCH v2 9/9] ksmbd: avoid self oplock breaks for EA opens
From: Ze Tan
Date: Thu Jul 16 2026 - 23:42:37 EST
generic/093 writes to a file after setcap sets security.capability. The
CIFS client can reopen the file to query or update EAs while the same
session holds an oplock. ksmbd breaks this oplock and waits for the same
client, so the write can block.
Skip the oplock break for same-session EA or metadata opens. Keep the
normal break when another session holds an oplock.
Signed-off-by: Ze Tan <tanze@xxxxxxxxxx>
---
fs/smb/server/oplock.c | 49 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 3c55ae5d6a11..85f7c29fc2c4 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -1424,6 +1424,49 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
ksmbd_inode_put(p_ci);
}
+static bool ksmbd_skip_session_ea_break(struct ksmbd_work *work,
+ struct ksmbd_file *fp,
+ int req_op_level,
+ struct lease_ctx_info *lctx)
+{
+ struct oplock_info *opinfo;
+ bool same_session = false;
+ bool foreign_session = false;
+ __le32 ea_meta_mask = FILE_READ_EA_LE | FILE_WRITE_EA_LE |
+ FILE_READ_ATTRIBUTES_LE |
+ FILE_WRITE_ATTRIBUTES_LE |
+ FILE_READ_CONTROL_LE | FILE_SYNCHRONIZE_LE;
+
+ if (!work || !fp || lctx || req_op_level != SMB2_OPLOCK_LEVEL_NONE)
+ return false;
+
+ /*
+ * Avoid self-deadlock when the same session reopens the file only
+ * to read/write EAs or metadata, e.g. CIFS killpriv querying or
+ * removing security.capability during buffered write.
+ */
+ if (!(fp->daccess & (FILE_READ_EA_LE | FILE_WRITE_EA_LE)) ||
+ (fp->daccess & ~ea_meta_mask))
+ return false;
+
+ down_read(&fp->f_ci->m_lock);
+ list_for_each_entry(opinfo, &fp->f_ci->m_op_list, op_entry) {
+ if (!opinfo->conn || opinfo->level == SMB2_OPLOCK_LEVEL_NONE)
+ continue;
+
+ if (opinfo->sess == work->sess)
+ same_session = true;
+ else
+ foreign_session = true;
+
+ if (same_session && foreign_session)
+ break;
+ }
+ up_read(&fp->f_ci->m_lock);
+
+ return same_session && !foreign_session;
+}
+
/**
* smb_grant_oplock() - handle oplock/lease request on file open
* @work: smb work
@@ -1529,6 +1572,12 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
goto err_out;
}
+ if (share_ret >= 0 &&
+ ksmbd_skip_session_ea_break(work, fp, req_op_level, lctx)) {
+ opinfo_put(prev_opinfo);
+ goto set_lev;
+ }
+
if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
opinfo_put(prev_opinfo);
--
2.43.0