get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2229606/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2229606,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2229606/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/20260428140856.941847-3-charsyam@gmail.com/",
    "project": {
        "id": 12,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/12/?format=api",
        "name": "Linux CIFS Client",
        "link_name": "linux-cifs-client",
        "list_id": "linux-cifs.vger.kernel.org",
        "list_email": "linux-cifs@vger.kernel.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260428140856.941847-3-charsyam@gmail.com>",
    "date": "2026-04-28T14:08:55",
    "name": "[v2,2/3] ksmbd: harden file lifetime during session teardown",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "dd75871a9feaaf5d70ab2d84d8bb0ffbc571e10f",
    "submitter": {
        "id": 93166,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/93166/?format=api",
        "name": "DaeMyung Kang",
        "email": "charsyam@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/20260428140856.941847-3-charsyam@gmail.com/mbox/",
    "series": [
        {
            "id": 501865,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/501865/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/list/?series=501865",
            "date": "2026-04-28T14:08:55",
            "name": "ksmbd: fix connection and durable handle teardown races",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/501865/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2229606/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2229606/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <linux-cifs+bounces-11218-incoming=patchwork.ozlabs.org@vger.kernel.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "linux-cifs@vger.kernel.org"
        ],
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=jFUrSbDg;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.232.135.74; helo=sto.lore.kernel.org;\n envelope-from=linux-cifs+bounces-11218-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)",
            "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=\"jFUrSbDg\"",
            "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.210.175",
            "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com",
            "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=gmail.com"
        ],
        "Received": [
            "from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g4j6D59bXz1xvV\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 29 Apr 2026 00:11:32 +1000 (AEST)",
            "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id D75113035B4C\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 14:09:26 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 8BB9E449EAB;\n\tTue, 28 Apr 2026 14:09:09 +0000 (UTC)",
            "from mail-pf1-f175.google.com (mail-pf1-f175.google.com\n [209.85.210.175])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id C879E441036\n\tfor <linux-cifs@vger.kernel.org>; Tue, 28 Apr 2026 14:09:07 +0000 (UTC)",
            "by mail-pf1-f175.google.com with SMTP id\n d2e1a72fcca58-823be54d49cso856970b3a.3\n        for <linux-cifs@vger.kernel.org>;\n Tue, 28 Apr 2026 07:09:07 -0700 (PDT)",
            "from ser8.. ([221.156.231.192])\n        by smtp.gmail.com with ESMTPSA id\n 41be03b00d2f7-c7fcade0f56sm2190526a12.21.2026.04.28.07.09.05\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Tue, 28 Apr 2026 07:09:06 -0700 (PDT)"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1777385349; cv=none;\n b=iSOLd1PIIGAHpOugIRm5w7NJdjv4fqbbeh9ndxg9LQL/cil1citeKsa2dq6NzMm4WlLAIuFDETjXpAliWxkojQdch6TH2qqQoIXBo4F8NLQ/JzHSHhIWkVyYILHv+e78Xrmdex1bAMAG/JYEsxLLvWu+etjIhC0xczQ45uwOoJ8=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1777385349; c=relaxed/simple;\n\tbh=7Y75CJc2/d6aHB2cQnB6UNXLLb91D24toWSFpWWyhQc=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=d+g6XroKHYA4ukS1FkIK5sKtE8EqAF0UM4eUEm3ASDHt2MTCCG113xne9O8wKgqAwSPR0zo7sUxelf1flq9iZErAGqeBjdeiQL+aHVeRu48DIT0bocqrRErKgi3T4Mg1xOCa4KeBbsid3WkeTZhFXqR96q0qbo2nc16wEUHvdfs=",
        "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com;\n spf=pass smtp.mailfrom=gmail.com;\n dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=jFUrSbDg; arc=none smtp.client-ip=209.85.210.175",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1777385347; x=1777990147;\n darn=vger.kernel.org;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=xlWHHqWJasISzgKRTEELiwHwHgmZ46NRjOQ3vo3IPy4=;\n        b=jFUrSbDg/4oC2PkH4sl2GhXb7QY8/XCc0gAnSxpwC1ofvASx9wclP7c9dbcJ1w1goR\n         zle6Uj5MGCAdyJZgOp+RUjObCE0EcJ6VHzHNhUbjw3nVnsA1UVVhiYlmVdhveF2G5bon\n         cVVj8he/FVRD58nl+xOaGKJg/niRl7gsNpxaxH+g1nY8uxcqJ12l+118jIc/qZQC0sTG\n         3KBXWZOEl1HAj7akx3uXT8+lxUrfOXP9Uq7KfQ7s9Ou6z1g0n+uGjoi3FynXCTiL1+oF\n         gw8SYTxM5a4BwCtgRPg8FPjiqPQHGYQng79zzJlWyWC+cQ3OmCOpEp7yiTi/wOkrPZEI\n         +iHA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1777385347; x=1777990147;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n         :to:cc:subject:date:message-id:reply-to;\n        bh=xlWHHqWJasISzgKRTEELiwHwHgmZ46NRjOQ3vo3IPy4=;\n        b=S6ZxOTjZ8ALnoTeSbfeB5S75+6r7C/tRVU0Ycp1S/uD/oIGVY2DWjLUbmuVZjr7AmO\n         Frb6TkMXEtSeBX2oTg7cgmvkDeWtLeSfWyWIsu9YbeBPpVjgnioyjeMg/wy/Nk1v2bX9\n         uOFvHTKUIDpc8A7/jn7HiYQ2+L+LOj18S30TWUyTNYMaL8+UnZzVytLfkfvFf0oXYG3f\n         CNNT/PSb/HPPRYhIi7kRlBf5HMKCjNc0nOQ14WHWsH/KMERq1tBuNqQkqezVjSyNV45R\n         gTGdD8+kVeiGL0GGuymBlzEmmKJf1SgBVK+KMIJcPpMfA8+MUfmq+77vstlsBMU3KE8x\n         P+Zg==",
        "X-Forwarded-Encrypted": "i=1;\n AFNElJ+/2WRnPUguH0Rgovie9nbgSsjLhQ+EG/9GHnlmNAS6cKp2Jwlu5+jpaLi6Qv8hfOB64zfw5fCQEtkE@vger.kernel.org",
        "X-Gm-Message-State": "AOJu0YyIWKDMunLTPJHJmRMCGrn0YiN8nXrWJf0jSpT7wT10qHLLH0O9\n\tweye1/v3coPw4y3ATcHUKUteQh8F1wAgZv9EpPBt/kZC2E14XSsDJaHD",
        "X-Gm-Gg": "AeBDievZHEWYYj3nizbExvvyjoABNjX8t1pAL1HtkFDYn/uOR9N5cR5KJ11x85n2Uun\n\tW2gJWDnLT9mJfOeFo5V9KSc4RhvVWkluGpAIKQRXmYRa2GfYv8vvcYijbcn+1I4TJqtHuLk5Js5\n\tfL8b0rX1S8tIf4wBGm0rhISfumkBxJit1YcesIQDI46325pdWyFkeSKCbIvMO5qbb2enle4uQYC\n\tTmEoW29zAb2FKNG+qlN/qbnsghTSyhr9aMag1fiFlGpVKm8b2WpfvPdD36VVz8fyx44Y0RaJOrY\n\tXJwH3zgrp4AUQngomI4GdlBStpCbybW/n9iXhQwOJlppucc7S1pOV1+ewgaJejwFXABf5R0ry0i\n\tY8V63BAwenTDDcwtsEkJ+GLXU32mP06GxWH9XtSVG9p1mWmw4aE99n+WUivP4UnyUOMJsuhUjFn\n\tEkAEsRw0KuUMsXJpm5UIXNa5MgVrE=",
        "X-Received": "by 2002:a05:6a21:10e:b0:3a2:c72c:745d with SMTP id\n adf61e73a8af0-3a398db06e1mr2710162637.4.1777385346798;\n        Tue, 28 Apr 2026 07:09:06 -0700 (PDT)",
        "From": "DaeMyung Kang <charsyam@gmail.com>",
        "To": "Namjae Jeon <linkinjeon@kernel.org>,\n\tSteve French <smfrench@gmail.com>",
        "Cc": "Sergey Senozhatsky <senozhatsky@chromium.org>,\n\tTom Talpey <tom@talpey.com>,\n\tHyunchul Lee <hyc.lee@gmail.com>,\n\tRonnie Sahlberg <lsahlber@redhat.com>,\n\tlinux-cifs@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org,\n\tDaeMyung Kang <charsyam@gmail.com>",
        "Subject": "[PATCH v2 2/3] ksmbd: harden file lifetime during session teardown",
        "Date": "Tue, 28 Apr 2026 23:08:55 +0900",
        "Message-ID": "<20260428140856.941847-3-charsyam@gmail.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "In-Reply-To": "<20260428140856.941847-1-charsyam@gmail.com>",
        "References": "<20260428140856.941847-1-charsyam@gmail.com>",
        "Precedence": "bulk",
        "X-Mailing-List": "linux-cifs@vger.kernel.org",
        "List-Id": "<linux-cifs.vger.kernel.org>",
        "List-Subscribe": "<mailto:linux-cifs+subscribe@vger.kernel.org>",
        "List-Unsubscribe": "<mailto:linux-cifs+unsubscribe@vger.kernel.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit"
    },
    "content": "__close_file_table_ids() is the per-session teardown that closes every\nfp belonging to a session (or to one tree connect on that session) by\nwalking the session's volatile-id idr.  The current loop has three\nrelated problems on busy or racing workloads:\n\n  * Sleeping under ft->lock.  The session-teardown skip callback,\n    session_fd_check(), already sleeps in ksmbd_vfs_copy_durable_owner()\n    -> kstrdup(GFP_KERNEL) and down_write(&fp->f_ci->m_lock) (a\n    rw_semaphore).  Running the callback inside write_lock(&ft->lock)\n    trips CONFIG_DEBUG_ATOMIC_SLEEP / CONFIG_PROVE_LOCKING on a\n    durable-fd workload.\n\n  * Refcount accounting blind to f_state.  The unconditional\n    atomic_dec_and_test(&fp->refcount) does not distinguish\n    FP_INITED (idr-owned reference still intact) from FP_CLOSED (an\n    earlier ksmbd_close_fd() already consumed the idr-owned reference\n    while leaving fp in the idr because a holder kept refcount\n    non-zero).  When the latter races with teardown the same path\n    over-decrements into a holder reference and ksmbd_fd_put() later\n    UAFs that holder.\n\n  * FP_NEW window.  Between __open_id() publishing fp into the\n    session idr and ksmbd_update_fstate(..., FP_INITED) committing the\n    transition at the end of smb2_open(), an fp is in FP_NEW and an\n    intervening teardown that takes a transient reference and\n    unpublishes the volatile id leaves the original idr-owned\n    reference orphaned -- the opener is unaware that fp has been\n    unpublished, returns success to the client, and the fp leaks at\n    refcount = 1.\n\nRefactor __close_file_table_ids() to take a transient reference on fp\nand unpublish fp from the session idr *under ft->lock* before calling\nskip() outside the lock.  A transient ref protects lifetime but not\nconcurrent field mutation, so the idr_remove() is what keeps\n__ksmbd_lookup_fd() through this session's idr from granting a new\nksmbd_fp_get() reference to an fp whose fp->conn / fp->tcon /\nfp->volatile_id / op->conn / lock_list links are about to be rewritten\nby session_fd_check().  Durable reconnect is unaffected because it\nreaches fp through the global durable table (ksmbd_lookup_durable_fd\n-> global_ft).\n\nDecide n_to_drop together with any FP_INITED -> FP_CLOSED transition\nunder ft->lock so teardown and ksmbd_close_fd() never both consume the\nidr-owned reference.  See ksmbd_mark_fp_closed() for the per-state\naccounting.  For the FP_NEW path to be safe, the opener has to learn\nthat fp was unpublished: ksmbd_update_fstate() now returns -ENOENT\nwhen an FP_NEW -> FP_INITED transition finds f_state already advanced\nor the volatile id cleared (both committed by teardown under\nft->lock); smb2_open() propagates that as STATUS_OBJECT_NAME_INVALID\nand drops the original reference via ksmbd_fd_put().\n\nThe list removal cannot be left for a deferred final putter because\nfp->volatile_id has already been cleared and __ksmbd_remove_fd() will\nintentionally skip both idr_remove() and list_del_init().  Move the\nm_fp_list unlink in __ksmbd_remove_fd() above the volatile-id check so\nthat an FP_NEW fp that happened to be added to m_fp_list (smb2_open()\nadds fp->node before ksmbd_update_fstate() runs) is still cleaned up\non the deferred putter path; list_del_init() on an empty node is a\nno-op and remains safe for fps that were never added.\n\nAdd a defensive guard in session_fd_check() that refuses non-FP_INITED\nfps so that even if a teardown reaches an FP_NEW fp it falls into the\nclose branch (where the n_to_drop = 1 accounting keeps the opener's\nreference alive) instead of the durable-preserve branch (which mutates\nfp->conn / fp->tcon).\n\nValidation on a debug kernel additionally built with CONFIG_DEBUG_LIST\nand CONFIG_DEBUG_OBJECTS_WORK used a same-session two-tcon workload\n(open/write storm on one tcon, 50 tree disconnects on the other) and\nreported no list-corruption, work_struct ODEBUG, sleep-in-atomic,\nlockdep or kmemleak reports.  Reverting only the\n__close_file_table_ids() hunk while keeping a forced-is_reconnectable()\nharness produced the expected sleep-in-atomic at vfs_cache.c:1095,\nconfirming the ft->lock-out-of-sleepable-skip discipline.\n\nKASAN-enabled direct SMB2 coverage with durable handles enabled\nexercised ksmbd_close_tree_conn_fds(), ksmbd_close_session_fds(),\nthe FP_NEW failure path, tree_conn_fd_check(), and a non-zero\nsession_fd_check() durable-preserve return.  This produced no KASAN,\nDEBUG_LIST, ODEBUG, or WARNING reports.\n\nFixes: f44158485826 (\"cifsd: add file operations\")\nSigned-off-by: DaeMyung Kang <charsyam@gmail.com>\n---\n fs/smb/server/smb2pdu.c   |   6 +-\n fs/smb/server/vfs_cache.c | 179 +++++++++++++++++++++++++++++++++-----\n fs/smb/server/vfs_cache.h |   4 +-\n 3 files changed, 164 insertions(+), 25 deletions(-)",
    "diff": "diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c\nindex 21825a69c29a..c1240ca14253 100644\n--- a/fs/smb/server/smb2pdu.c\n+++ b/fs/smb/server/smb2pdu.c\n@@ -3767,8 +3767,10 @@ int smb2_open(struct ksmbd_work *work)\n \n err_out2:\n \tif (!rc) {\n-\t\tksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);\n-\t\trc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);\n+\t\trc = ksmbd_update_fstate(&work->sess->file_table, fp,\n+\t\t\t\t\t FP_INITED);\n+\t\tif (!rc)\n+\t\t\trc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);\n \t}\n \tif (rc) {\n \t\tif (rc == -EINVAL)\ndiff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c\nindex 4a18107937cc..dc4037ef1834 100644\n--- a/fs/smb/server/vfs_cache.c\n+++ b/fs/smb/server/vfs_cache.c\n@@ -431,13 +431,13 @@ static void ksmbd_remove_durable_fd(struct ksmbd_file *fp)\n \n static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)\n {\n-\tif (!has_file_id(fp->volatile_id))\n-\t\treturn;\n-\n \tdown_write(&fp->f_ci->m_lock);\n \tlist_del_init(&fp->node);\n \tup_write(&fp->f_ci->m_lock);\n \n+\tif (!has_file_id(fp->volatile_id))\n+\t\treturn;\n+\n \twrite_lock(&ft->lock);\n \tidr_remove(ft->idr, fp->volatile_id);\n \twrite_unlock(&ft->lock);\n@@ -798,15 +798,58 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)\n \treturn ERR_PTR(ret);\n }\n \n-void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,\n-\t\t\t unsigned int state)\n+/**\n+ * ksmbd_update_fstate() - update an fp state under the file-table lock\n+ * @ft: file table that publishes @fp's volatile id\n+ * @fp: file pointer to update\n+ * @state: new state\n+ *\n+ * Return: 0 on success.  The FP_NEW -> FP_INITED transition is special:\n+ * -ENOENT if teardown already unpublished @fp by advancing the state or\n+ * clearing the volatile id.  Other state updates preserve the historical\n+ * fire-and-forget behavior.\n+ */\n+int ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,\n+\t\t\tunsigned int state)\n {\n+\tint ret;\n+\n \tif (!fp)\n-\t\treturn;\n+\t\treturn -ENOENT;\n \n \twrite_lock(&ft->lock);\n-\tfp->f_state = state;\n+\tif (state == FP_INITED &&\n+\t    (fp->f_state != FP_NEW || !has_file_id(fp->volatile_id))) {\n+\t\tret = -ENOENT;\n+\t} else {\n+\t\tfp->f_state = state;\n+\t\tret = 0;\n+\t}\n \twrite_unlock(&ft->lock);\n+\n+\treturn ret;\n+}\n+\n+/*\n+ * ksmbd_mark_fp_closed() - mark fp closed under ft->lock and return how many\n+ * refs the teardown path owns.\n+ *\n+ * FP_INITED has a normal idr-owned reference, so teardown owns both that\n+ * reference and the transient lookup reference.  FP_NEW is still owned by the\n+ * in-flight opener/reopener, which will drop the original reference after\n+ * ksmbd_update_fstate(..., FP_INITED) observes the cleared volatile id.\n+ * FP_CLOSED on entry means an earlier ksmbd_close_fd() already consumed the\n+ * idr-owned ref.\n+ */\n+static int ksmbd_mark_fp_closed(struct ksmbd_file *fp)\n+{\n+\tif (fp->f_state == FP_INITED) {\n+\t\tset_close_state_blocked_works(fp);\n+\t\tfp->f_state = FP_CLOSED;\n+\t\treturn 2;\n+\t}\n+\n+\treturn 1;\n }\n \n static int\n@@ -814,7 +857,8 @@ __close_file_table_ids(struct ksmbd_session *sess,\n \t\t       struct ksmbd_tree_connect *tcon,\n \t\t       bool (*skip)(struct ksmbd_tree_connect *tcon,\n \t\t\t\t    struct ksmbd_file *fp,\n-\t\t\t\t    struct ksmbd_user *user))\n+\t\t\t\t    struct ksmbd_user *user),\n+\t\t       bool skip_preserves_fp)\n {\n \tstruct ksmbd_file_table *ft = &sess->file_table;\n \tstruct ksmbd_file *fp;\n@@ -822,32 +866,120 @@ __close_file_table_ids(struct ksmbd_session *sess,\n \tint num = 0;\n \n \twhile (1) {\n+\t\tint n_to_drop;\n+\n \t\twrite_lock(&ft->lock);\n \t\tfp = idr_get_next(ft->idr, &id);\n \t\tif (!fp) {\n \t\t\twrite_unlock(&ft->lock);\n \t\t\tbreak;\n \t\t}\n-\n-\t\tif (skip(tcon, fp, sess->user) ||\n-\t\t    !atomic_dec_and_test(&fp->refcount)) {\n+\t\tif (!atomic_inc_not_zero(&fp->refcount)) {\n \t\t\tid++;\n \t\t\twrite_unlock(&ft->lock);\n \t\t\tcontinue;\n \t\t}\n \n-\t\tset_close_state_blocked_works(fp);\n-\t\tidr_remove(ft->idr, fp->volatile_id);\n-\t\tfp->volatile_id = KSMBD_NO_FID;\n-\t\twrite_unlock(&ft->lock);\n+\t\tif (skip_preserves_fp) {\n+\t\t\t/*\n+\t\t\t * Session teardown: skip() is session_fd_check(),\n+\t\t\t * which may sleep and mutates fp->conn / fp->tcon /\n+\t\t\t * fp->volatile_id when it chooses to preserve fp\n+\t\t\t * for durable reconnect.  Unpublish fp from the\n+\t\t\t * session idr here, under ft->lock, so that\n+\t\t\t * __ksmbd_lookup_fd() through this session cannot\n+\t\t\t * grant a new ksmbd_fp_get() reference to an fp\n+\t\t\t * whose fields are about to be rewritten outside\n+\t\t\t * the lock.  Durable reconnect still reaches fp via\n+\t\t\t * global_ft.\n+\t\t\t */\n+\t\t\tidr_remove(ft->idr, id);\n+\t\t\tfp->volatile_id = KSMBD_NO_FID;\n+\t\t\twrite_unlock(&ft->lock);\n+\n+\t\t\tif (skip(tcon, fp, sess->user)) {\n+\t\t\t\t/*\n+\t\t\t\t * session_fd_check() has converted fp to\n+\t\t\t\t * durable-preserve state and cleared its\n+\t\t\t\t * per-conn fields.  fp is already unpublished\n+\t\t\t\t * above; the original idr-owned ref keeps it\n+\t\t\t\t * alive for the durable scavenger.  Drop only\n+\t\t\t\t * the transient ref.  atomic_dec() is safe --\n+\t\t\t\t * atomic_inc_not_zero() succeeded on a\n+\t\t\t\t * positive value and we added one more, so\n+\t\t\t\t * refcount cannot be zero here.\n+\t\t\t\t */\n+\t\t\t\tatomic_dec(&fp->refcount);\n+\t\t\t\tid++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * Keep the close-state decision under the same lock\n+\t\t\t * observed by ksmbd_update_fstate(), which is how an\n+\t\t\t * in-flight FP_NEW opener learns that teardown has\n+\t\t\t * cleared its volatile id.\n+\t\t\t */\n+\t\t\twrite_lock(&ft->lock);\n+\t\t\tn_to_drop = ksmbd_mark_fp_closed(fp);\n+\t\t\twrite_unlock(&ft->lock);\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * Tree teardown: skip() is tree_conn_fd_check(), a\n+\t\t\t * cheap pointer compare that doesn't sleep and has\n+\t\t\t * no side effects, so keep the skip decision plus\n+\t\t\t * the unpublish-and-mark-closed sequence atomic\n+\t\t\t * under ft->lock.  fps belonging to other tree\n+\t\t\t * connects (skip() == true) stay fully published in\n+\t\t\t * the session idr with no lock window.\n+\t\t\t */\n+\t\t\tif (skip(tcon, fp, sess->user)) {\n+\t\t\t\tatomic_dec(&fp->refcount);\n+\t\t\t\twrite_unlock(&ft->lock);\n+\t\t\t\tid++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tidr_remove(ft->idr, id);\n+\t\t\tfp->volatile_id = KSMBD_NO_FID;\n+\t\t\tn_to_drop = ksmbd_mark_fp_closed(fp);\n+\t\t\twrite_unlock(&ft->lock);\n+\t\t}\n \n+\t\t/*\n+\t\t * fp->volatile_id is already cleared to prevent stale idr\n+\t\t * removal from a deferred final close.  Remove fp from\n+\t\t * m_fp_list here because __ksmbd_remove_fd() will skip the\n+\t\t * list unlink when volatile_id is KSMBD_NO_FID.\n+\t\t */\n \t\tdown_write(&fp->f_ci->m_lock);\n \t\tlist_del_init(&fp->node);\n \t\tup_write(&fp->f_ci->m_lock);\n \n-\t\t__ksmbd_close_fd(ft, fp);\n-\n-\t\tnum++;\n+\t\t/*\n+\t\t * Drop the references this iteration owns:\n+\t\t *\n+\t\t *   n_to_drop == 2: we observed FP_INITED and committed\n+\t\t *     the FP_CLOSED transition ourselves, so we own the\n+\t\t *     transient (+1) and the still-intact idr-owned ref.\n+\t\t *\n+\t\t *   n_to_drop == 1: either a prior ksmbd_close_fd()\n+\t\t *     already consumed the idr-owned ref, or fp was still\n+\t\t *     FP_NEW and the in-flight opener/reopener must keep\n+\t\t *     the original reference until ksmbd_update_fstate()\n+\t\t *     observes the cleared volatile id.\n+\t\t *\n+\t\t * If we end up as the final putter, finalize fp and\n+\t\t * account the open_files_count decrement via the caller's\n+\t\t * atomic_sub(num, ...).  Otherwise the remaining user's\n+\t\t * ksmbd_fd_put() reaches __put_fd_final(), which does its\n+\t\t * own atomic_dec(&open_files_count), so we must not count\n+\t\t * this fp here -- doing so would double-decrement the\n+\t\t * connection-wide counter.\n+\t\t */\n+\t\tif (atomic_sub_and_test(n_to_drop, &fp->refcount)) {\n+\t\t\t__ksmbd_close_fd(NULL, fp);\n+\t\t\tnum++;\n+\t\t}\n \t\tid++;\n \t}\n \n@@ -1082,6 +1214,9 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,\n \tif (!is_reconnectable(fp))\n \t\treturn false;\n \n+\tif (fp->f_state != FP_INITED)\n+\t\treturn false;\n+\n \tif (WARN_ON_ONCE(!fp->conn))\n \t\treturn false;\n \n@@ -1127,7 +1262,8 @@ void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)\n {\n \tint num = __close_file_table_ids(work->sess,\n \t\t\t\t\t work->tcon,\n-\t\t\t\t\t tree_conn_fd_check);\n+\t\t\t\t\t tree_conn_fd_check,\n+\t\t\t\t\t false);\n \n \tatomic_sub(num, &work->conn->stats.open_files_count);\n }\n@@ -1136,7 +1272,8 @@ void ksmbd_close_session_fds(struct ksmbd_work *work)\n {\n \tint num = __close_file_table_ids(work->sess,\n \t\t\t\t\t work->tcon,\n-\t\t\t\t\t session_fd_check);\n+\t\t\t\t\t session_fd_check,\n+\t\t\t\t\t true);\n \n \tatomic_sub(num, &work->conn->stats.open_files_count);\n }\n@@ -1268,7 +1405,7 @@ void ksmbd_destroy_file_table(struct ksmbd_session *sess)\n \tif (!ft->idr)\n \t\treturn;\n \n-\t__close_file_table_ids(sess, NULL, session_fd_check);\n+\t__close_file_table_ids(sess, NULL, session_fd_check, true);\n \tidr_destroy(ft->idr);\n \tkfree(ft->idr);\n \tft->idr = NULL;\ndiff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h\nindex 866f32c10d4d..e6871266a94b 100644\n--- a/fs/smb/server/vfs_cache.h\n+++ b/fs/smb/server/vfs_cache.h\n@@ -172,8 +172,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);\n int ksmbd_init_global_file_table(void);\n void ksmbd_free_global_file_table(void);\n void ksmbd_set_fd_limit(unsigned long limit);\n-void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,\n-\t\t\t unsigned int state);\n+int ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,\n+\t\t\tunsigned int state);\n bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,\n \t\tstruct ksmbd_user *user);\n \n",
    "prefixes": [
        "v2",
        "2/3"
    ]
}