From nobody Tue Dec 2 02:19:57 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7BAE3612E9; Wed, 19 Nov 2025 13:42:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763559763; cv=none; b=KxsmOdzLJJ4j8N2biA1ifO5Vc2Ux1LCpcblcW2h2uaFqaSm0PKn5Wo9IOjBZMzRC6qy2WmVDq2wEzIF9kW//2VJAfWO6HIxB6nEgDAOh7Be1Y8FkR4X82xiyeTxCHhm1wEFLcPll/0QeNC9R/eoJsEoDUhf3N6Kggrje6CP68iI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763559763; c=relaxed/simple; bh=bOsgYjPeUMmPlZY4cRL+96QYFL7r0WVAw5+qEW5Suvo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y/KpCMWrax3vuSD8GxiAGrsM6tY6TkoOsBXhS3M+6aFwUbrT+CQYS4f69QEkNwjswYYrpS5Mu9V4qbW+gRkT87jNGMPzgWBjH+jEFSkNR5aK+TTCjKGPPVORHlDSA4VMkjgW7mZc8UvItGmrpkxUyQRXfecOh4RGOl0RuZG5tmw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ktexG/gu; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ktexG/gu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B00DCC2BCB6; Wed, 19 Nov 2025 13:42:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763559762; bh=bOsgYjPeUMmPlZY4cRL+96QYFL7r0WVAw5+qEW5Suvo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ktexG/gu+z6XOVhONgGEnD7abxAfddd09s5oKjiVzfcugnDli9klLcxc/2YdAxNUb UVgfsjSGiGQ8G/h1zqZLWUI1auozQwEpVBezxJBcCOPnFmCPI7wwy0dNWg7QnSQ6/J O2B+6Uw22hdUXsUG8tk3fN0LnkNKAqWsgl3ZdGkaQagO5dYn+VcIlOVgQuV8MZZf0T CMKNKLkmSaOIPHj4DwidUPUCprDZmrQhyYXR0Y78DC6ckN9NnGErHtV5lLBsX3fFFk J6hAychofZXBymPZIXkEqt5PhXPiKmMeSTHhPOW+mqcS/E/eM8fApyVpPCBXvjtAOy Ik+7LPTVfqNOw== From: Jeff Layton Date: Wed, 19 Nov 2025 08:42:18 -0500 Subject: [PATCH v8 1/3] vfs: expose delegation support to userland Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251119-dir-deleg-ro-v8-1-81b6cf5485c6@kernel.org> References: <20251119-dir-deleg-ro-v8-0-81b6cf5485c6@kernel.org> In-Reply-To: <20251119-dir-deleg-ro-v8-0-81b6cf5485c6@kernel.org> To: Alexander Viro , Christian Brauner , Jan Kara , Jeff Layton , Chuck Lever , Alexander Aring , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-nfs@vger.kernel.org, Stephen Rothwell X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8057; i=jlayton@kernel.org; h=from:subject:message-id; bh=bOsgYjPeUMmPlZY4cRL+96QYFL7r0WVAw5+qEW5Suvo=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpHclPCvJ5fmEIyYv3UXwu8nHA70rNSfvoCSU9S dVvU8TyOjyJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaR3JTwAKCRAADmhBGVaC Fb3xEADDya80yW/Om5bTNCsIwU6GnQKVhTTWAs/UBbdLSAhfpjFlKi1hoIjGlGiWh9ZT+u2qtXV PPkr4yZpxzlXpogyRuitbr8jjr7lb9qJQtydxcGkj23BEbQ6PXGEsi+sX3I4vnLnPUpGZD45LGt 0jqUDiG/yRb7HuToswrJtsVRQ04FgT+IdtpKzIe7OFlHOR1wub/xEcwhrPAhUrsSU3AGVre4Mpp 7W7XM0v6ZMDandGkUHrRxHaj9xiTogyA/03xvP0k3Ep2qu9Rjd8H0C3BwjXVnMUpXI/bRhcM72P WtSMhT98T7EOWZW63+w7zaH03PBVwiBDLfOc96GlgVkSUDCMVPYGmxLiHfVNt3Z/rmcRd9ez5WW opgq7pmlAKJ1vxH+CVytXDKIt+QTHtS9uZQJT1HhzY888EmzjTKxEnbTAM3m8En6kujOG73WJtq a3txsWVfi306svANoE13i17Y264/ZucAbp1SkD/0oxrrd2k/6Q6ut13zJzsMH7pcBE8rZGHJipA p37PdOak1YunT1MElZolsy8KqtYy5DfH/dXIc06BmVNwIsMmFoVIYLs6UjUpuUg9qMiTrc7Xbft KuAja0MkTqmLU3HOk5505Dy27KmHtlemChPxpfevisJXqYseHRng9umpIxrbddM/Vjmz2048K/B 5gUwWl8D5f1v8Sw== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Now that support for recallable directory delegations is available, expose this functionality to userland with new F_SETDELEG and F_GETDELEG commands for fcntl(). Note that this also allows userland to request a FL_DELEG type lease on files too. Userland applications that do will get signalled when there are metadata changes in addition to just data changes (which is a limitation of FL_LEASE leases). These commands accept a new "struct delegation" argument that contains a flags field for future expansion. Signed-off-by: Jeff Layton Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-17-52f3feebb2f2@ker= nel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/fcntl.c | 13 +++++++ fs/locks.c | 94 ++++++++++++++++++++++++++++++++++--------= ---- include/linux/filelock.h | 12 ++++++ include/uapi/linux/fcntl.h | 16 ++++++++ 4 files changed, 110 insertions(+), 25 deletions(-) diff --git a/fs/fcntl.c b/fs/fcntl.c index 72f8433d9109889eecef56b32d20a85b4e12ea44..f93dbca0843557d197bd1e02351= 9cfa0f00ad78f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -445,6 +445,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned= long arg, struct file *filp) { void __user *argp =3D (void __user *)arg; + struct delegation deleg; int argi =3D (int)arg; struct flock flock; long err =3D -EINVAL; @@ -550,6 +551,18 @@ static long do_fcntl(int fd, unsigned int cmd, unsigne= d long arg, case F_SET_RW_HINT: err =3D fcntl_set_rw_hint(filp, arg); break; + case F_GETDELEG: + if (copy_from_user(&deleg, argp, sizeof(deleg))) + return -EFAULT; + err =3D fcntl_getdeleg(filp, &deleg); + if (!err && copy_to_user(argp, &deleg, sizeof(deleg))) + return -EFAULT; + break; + case F_SETDELEG: + if (copy_from_user(&deleg, argp, sizeof(deleg))) + return -EFAULT; + err =3D fcntl_setdeleg(fd, filp, &deleg); + break; default: break; } diff --git a/fs/locks.c b/fs/locks.c index dd290a87f58eb5d522f03fa99d612fbad84dacf3..3df07871b5ab7bbe883cdd8fba8= 22d130282da8e 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1680,6 +1680,34 @@ void lease_get_mtime(struct inode *inode, struct tim= espec64 *time) } EXPORT_SYMBOL(lease_get_mtime); =20 +static int __fcntl_getlease(struct file *filp, unsigned int flavor) +{ + struct file_lease *fl; + struct inode *inode =3D file_inode(filp); + struct file_lock_context *ctx; + int type =3D F_UNLCK; + LIST_HEAD(dispose); + + ctx =3D locks_inode_context(inode); + if (ctx && !list_empty_careful(&ctx->flc_lease)) { + percpu_down_read(&file_rwsem); + spin_lock(&ctx->flc_lock); + time_out_leases(inode, &dispose); + list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) { + if (fl->c.flc_file !=3D filp) + continue; + if (fl->c.flc_flags & flavor) + type =3D target_leasetype(fl); + break; + } + spin_unlock(&ctx->flc_lock); + percpu_up_read(&file_rwsem); + + locks_dispose_list(&dispose); + } + return type; +} + /** * fcntl_getlease - Enquire what lease is currently active * @filp: the file @@ -1705,29 +1733,24 @@ EXPORT_SYMBOL(lease_get_mtime); */ int fcntl_getlease(struct file *filp) { - struct file_lease *fl; - struct inode *inode =3D file_inode(filp); - struct file_lock_context *ctx; - int type =3D F_UNLCK; - LIST_HEAD(dispose); - - ctx =3D locks_inode_context(inode); - if (ctx && !list_empty_careful(&ctx->flc_lease)) { - percpu_down_read(&file_rwsem); - spin_lock(&ctx->flc_lock); - time_out_leases(inode, &dispose); - list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) { - if (fl->c.flc_file !=3D filp) - continue; - type =3D target_leasetype(fl); - break; - } - spin_unlock(&ctx->flc_lock); - percpu_up_read(&file_rwsem); + return __fcntl_getlease(filp, FL_LEASE); +} =20 - locks_dispose_list(&dispose); - } - return type; +/** + * fcntl_getdeleg - enquire what sort of delegation is active + * @filp: file to be tested + * @deleg: structure where the result is stored + * + * Returns 0 on success or errno on failure. On success, + * deleg->d_type will contain the type of currently set lease + * (F_RDLCK, F_WRLCK or F_UNLCK). + */ +int fcntl_getdeleg(struct file *filp, struct delegation *deleg) +{ + if (deleg->d_flags !=3D 0 || deleg->__pad !=3D 0) + return -EINVAL; + deleg->d_type =3D __fcntl_getlease(filp, FL_DELEG); + return 0; } =20 /** @@ -2039,13 +2062,13 @@ vfs_setlease(struct file *filp, int arg, struct fil= e_lease **lease, void **priv) } EXPORT_SYMBOL_GPL(vfs_setlease); =20 -static int do_fcntl_add_lease(unsigned int fd, struct file *filp, int arg) +static int do_fcntl_add_lease(unsigned int fd, struct file *filp, unsigned= int flavor, int arg) { struct file_lease *fl; struct fasync_struct *new; int error; =20 - fl =3D lease_alloc(filp, FL_LEASE, arg); + fl =3D lease_alloc(filp, flavor, arg); if (IS_ERR(fl)) return PTR_ERR(fl); =20 @@ -2081,7 +2104,28 @@ int fcntl_setlease(unsigned int fd, struct file *fil= p, int arg) =20 if (arg =3D=3D F_UNLCK) return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp); - return do_fcntl_add_lease(fd, filp, arg); + return do_fcntl_add_lease(fd, filp, FL_LEASE, arg); +} + +/** + * fcntl_setdeleg - sets a delegation on an open file + * @fd: open file descriptor + * @filp: file pointer + * @deleg: delegation request from userland + * + * Call this fcntl to establish a delegation on the file. + * Note that you also need to call %F_SETSIG to + * receive a signal when the lease is broken. + */ +int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *= deleg) +{ + /* For now, no flags are supported */ + if (deleg->d_flags !=3D 0 || deleg->__pad !=3D 0) + return -EINVAL; + + if (deleg->d_type =3D=3D F_UNLCK) + return vfs_setlease(filp, F_UNLCK, NULL, (void **)&filp); + return do_fcntl_add_lease(fd, filp, FL_DELEG, deleg->d_type); } =20 /** diff --git a/include/linux/filelock.h b/include/linux/filelock.h index 208d108df2d73a9df65e5dc9968d074af385f881..54b824c05299261e6bd6acc4175= cb277ea35b35d 100644 --- a/include/linux/filelock.h +++ b/include/linux/filelock.h @@ -159,6 +159,8 @@ int fcntl_setlk64(unsigned int, struct file *, unsigned= int, =20 int fcntl_setlease(unsigned int fd, struct file *filp, int arg); int fcntl_getlease(struct file *filp); +int fcntl_setdeleg(unsigned int fd, struct file *filp, struct delegation *= deleg); +int fcntl_getdeleg(struct file *filp, struct delegation *deleg); =20 static inline bool lock_is_unlock(struct file_lock *fl) { @@ -278,6 +280,16 @@ static inline int fcntl_getlease(struct file *filp) return F_UNLCK; } =20 +static inline int fcntl_setdeleg(unsigned int fd, struct file *filp, struc= t delegation *deleg) +{ + return -EINVAL; +} + +static inline int fcntl_getdeleg(struct file *filp, struct delegation *del= eg) +{ + return -EINVAL; +} + static inline bool lock_is_unlock(struct file_lock *fl) { return false; diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 3741ea1b73d8500061567b6590ccf5fb4c6770f0..5e277fd955aae50fa59e93f23d4= 62415ac0ca171 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -4,6 +4,11 @@ =20 #include #include +#ifdef __KERNEL__ +#include +#else +#include +#endif =20 #define F_SETLEASE (F_LINUX_SPECIFIC_BASE + 0) #define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1) @@ -79,6 +84,17 @@ */ #define RWF_WRITE_LIFE_NOT_SET RWH_WRITE_LIFE_NOT_SET =20 +/* Set/Get delegations */ +#define F_GETDELEG (F_LINUX_SPECIFIC_BASE + 15) +#define F_SETDELEG (F_LINUX_SPECIFIC_BASE + 16) + +/* Argument structure for F_GETDELEG and F_SETDELEG */ +struct delegation { + uint32_t d_flags; /* Must be 0 */ + uint16_t d_type; /* F_RDLCK, F_WRLCK, F_UNLCK */ + uint16_t __pad; /* Must be 0 */ +}; + /* * Types of directory notifications that may be requested. */ --=20 2.51.1 From nobody Tue Dec 2 02:19:57 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E45FE3624AF; Wed, 19 Nov 2025 13:42:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763559765; cv=none; b=U0budM7vHvit0H9I2fm6ALCudbKSfdDUMjsXoZSZI4LZJepryTqpLMlrnHSfu1obuIbL3cX1pLGUuoowyLAKfpE4uBnrFXKnfGEBsjbJSaYM5QVSbCHRd2nr5J12WoeXFDN8SJMJ9ylc4vq9Z6Afg0Fpw8c3LASTofOYsc9lE8s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763559765; c=relaxed/simple; bh=PFXUSzH9PyZbzkoKfXQR40e3WZl1vF7L9JQUzwpzrRI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=R8v+oE5GmrqOZ1U16as7G3Hto7xWPh80Tv0+jy2uDtO9Xxg0Q6zG7ttqJiLSBcCNsBPu2myNhPBrWELm/3qxXUT9vOmQv/jAD8tZkHj/LzI757dP3xBtl9/pLkWucujB954H1xnUlETXNObQtSIckxbzD7BRZIK0pk4f3YheBY8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oj3myy68; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="oj3myy68" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3B391C2BCB5; Wed, 19 Nov 2025 13:42:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763559764; bh=PFXUSzH9PyZbzkoKfXQR40e3WZl1vF7L9JQUzwpzrRI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=oj3myy687cd0Kw+vAWhO8XP92S7+mvJqyQee0ny5iVOAL4/9Qfqk192/xMaXCdG3i ok6sRQZyirGYMeC0gIgYTEtlF08T6veJoEno/pVAuWvI56l1NGPB+MRiuzvitIAFC9 CPHROrFAQov5AvvMn8xvOdyz1NBv96Xv1KntihA7c8C/h6/YRWioBN3apxPxY+8m4w +Eag7z/KpfL9DSoXnYeoDQpx2PoNfI4R82BlmRrGnqJYEezecBwrlDNhJuqj7TF1h5 Od12JMx6ReS3/jbSPkoi3HkhmA95Rf0sAS+STMqRZQfIXVF9oNcTX7tMA6lV+sg5cO A6QZ2Kel/vDBQ== From: Jeff Layton Date: Wed, 19 Nov 2025 08:42:19 -0500 Subject: [PATCH v8 2/3] filelock: add lease_dispose_list() helper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251119-dir-deleg-ro-v8-2-81b6cf5485c6@kernel.org> References: <20251119-dir-deleg-ro-v8-0-81b6cf5485c6@kernel.org> In-Reply-To: <20251119-dir-deleg-ro-v8-0-81b6cf5485c6@kernel.org> To: Alexander Viro , Christian Brauner , Jan Kara , Jeff Layton , Chuck Lever , Alexander Aring , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-nfs@vger.kernel.org, Stephen Rothwell X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=2909; i=jlayton@kernel.org; h=from:subject:message-id; bh=PFXUSzH9PyZbzkoKfXQR40e3WZl1vF7L9JQUzwpzrRI=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpHclPYYH8b53huilGRRAYY3ySpZuzfp3abUG4h 2h99muONzGJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaR3JTwAKCRAADmhBGVaC FVPbD/4oszGuwBkENOqF/rp8Q6iZ97dVAWYU3fCWPr/36tB7jW4eUXMVD8GdM8WG+StA5XWeP9A 8jZYyNkQV/BZ6P2T/55rtwiHOy9A3+13PDVAJu2qr6Bbfeek8rjMoum/1PyIUb4noS6ruwzWacZ T6cDCV8OKuAqu+w9WkmB3ZrphPLLNmSz5dx1VeDhvFMkrYhJYyn5sCIvpA4N16sDmCn/oQOjZeH +ViquFJXUMH54fTpd95FM79tjyqHECBTwDwc945lk2zaS5jM9rpT2KikZXi1FsP6UfbVvNGsXOw k9E3O9NsMWY0hFxbJ2pRvRMXEiUWnyK9xEwFMvFynJlDNi1jTAurWluLLgn+bPS5ZHiBgHOK4+Z WobTxPQijK7MZDCrw1jxjioppRF1MBo9bf2mrf2eXSA9hiZBxPeCma8qs5wzLvkROY+S0nFExiw PFMnPIiSQeNBMlYUkj7XG29NI5o8w7GqgCH8cWMnDxykiuiMMv1AHWLvqHNFDM7Tu1mD8esnbib KLvDp7zZATJsm4fyZHl/+HUr0/yH/E4ExpNG7+MCVw83bvVxrQyGwnNBAB/cSxA/yKwWWxSohf2 n+PLBti8h+HdUkQutjbQhW33hoWuqBs943BIiXE/5Y7K6IpurKXDynXo4kdJTmGEsrfxq2aanyC 3nCrnA5fCUr6Gjg== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 ...and call that from the lease handling code instead of locks_dispose_list(). Remove the lease handling parts from locks_dispose_list(). Signed-off-by: Jeff Layton --- fs/locks.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 3df07871b5ab7bbe883cdd8fba822d130282da8e..d4e6af6ac625204b337e94fd1e4= f6df2eee5cf50 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -369,10 +369,19 @@ locks_dispose_list(struct list_head *dispose) while (!list_empty(dispose)) { flc =3D list_first_entry(dispose, struct file_lock_core, flc_list); list_del_init(&flc->flc_list); - if (flc->flc_flags & (FL_LEASE|FL_DELEG|FL_LAYOUT)) - locks_free_lease(file_lease(flc)); - else - locks_free_lock(file_lock(flc)); + locks_free_lock(file_lock(flc)); + } +} + +static void +lease_dispose_list(struct list_head *dispose) +{ + struct file_lock_core *flc; + + while (!list_empty(dispose)) { + flc =3D list_first_entry(dispose, struct file_lock_core, flc_list); + list_del_init(&flc->flc_list); + locks_free_lease(file_lease(flc)); } } =20 @@ -1620,7 +1629,7 @@ int __break_lease(struct inode *inode, unsigned int f= lags) spin_unlock(&ctx->flc_lock); percpu_up_read(&file_rwsem); =20 - locks_dispose_list(&dispose); + lease_dispose_list(&dispose); error =3D wait_event_interruptible_timeout(new_fl->c.flc_wait, list_empty(&new_fl->c.flc_blocked_member), break_time); @@ -1643,7 +1652,7 @@ int __break_lease(struct inode *inode, unsigned int f= lags) out: spin_unlock(&ctx->flc_lock); percpu_up_read(&file_rwsem); - locks_dispose_list(&dispose); + lease_dispose_list(&dispose); free_lock: locks_free_lease(new_fl); return error; @@ -1703,7 +1712,7 @@ static int __fcntl_getlease(struct file *filp, unsign= ed int flavor) spin_unlock(&ctx->flc_lock); percpu_up_read(&file_rwsem); =20 - locks_dispose_list(&dispose); + lease_dispose_list(&dispose); } return type; } @@ -1904,7 +1913,7 @@ generic_add_lease(struct file *filp, int arg, struct = file_lease **flp, void **pr out: spin_unlock(&ctx->flc_lock); percpu_up_read(&file_rwsem); - locks_dispose_list(&dispose); + lease_dispose_list(&dispose); if (is_deleg) inode_unlock(inode); if (!error && !my_fl) @@ -1940,7 +1949,7 @@ static int generic_delete_lease(struct file *filp, vo= id *owner) error =3D fl->fl_lmops->lm_change(victim, F_UNLCK, &dispose); spin_unlock(&ctx->flc_lock); percpu_up_read(&file_rwsem); - locks_dispose_list(&dispose); + lease_dispose_list(&dispose); return error; } =20 @@ -2735,7 +2744,7 @@ locks_remove_lease(struct file *filp, struct file_loc= k_context *ctx) spin_unlock(&ctx->flc_lock); percpu_up_read(&file_rwsem); =20 - locks_dispose_list(&dispose); + lease_dispose_list(&dispose); } =20 /* --=20 2.51.1 From nobody Tue Dec 2 02:19:57 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 804E4361DDB; Wed, 19 Nov 2025 13:42:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763559766; cv=none; b=W3fS8fjb4fXyYPB7RM3JY3IBQ52sgQxvYVb064CzFfSPgqq7Sr1wcZTiIjdnP3cfUIG9jxCYDMWS9JOjhBpBCqiE3imHzja+OcefmkQ/Lci31B18tE+GB0MIBGLUbe1i7QGVjBeTnkSXyOJ90YDcON+xt/I0K/omfIPNkiCBZiA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763559766; c=relaxed/simple; bh=XmLiut+Qzx6ykbbfVHIfDiwFQuxqxqG6Krfa30IzFN4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=b6pe+6NOE9nuFq5ao2OWfBd7LCNdKaMkvXaVJiNneAvXMw9A0EFeCxU4npzVMEyHcpkfWUvVW+0lFg1RmHbrD9Van+5ZqLT4LlczogUStyU3+LYWmCG/yuAdahDWEaE2lShA5fCIhyxumdyVy+LJvPK1DNsI0WWNJbKAN42fRSE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=J4I/utKi; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="J4I/utKi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AA292C2BCB9; Wed, 19 Nov 2025 13:42:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763559765; bh=XmLiut+Qzx6ykbbfVHIfDiwFQuxqxqG6Krfa30IzFN4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=J4I/utKiYFzTSTByu2q3ZH5vKDMBbwD2BzgJch6vrnelw6B/K23rJtq+yOhic7G/X sJOB9Fz17xKEmOH/iaueQHPCJaSt+8wwy+YyKUjPyYkiSx/lxSIsQBXnAKnqmuKYAe n3m74ZzHIP66dfSEPjbwHfFWp9aJN6D212lw5BqdBdrDpV0K6bXIdTqSSZ4vdexZY+ dKWMAQ3Naxy2t17CIfn/glkj3msA0uECAWwpUcJUjTTJlgqDeE5ujgEbp7sW4DRE3F p1TxcPfUpTV0K3uglUBO/s1+2VKEJe0kjOGUWC4gd+231Kxflm8deaPcph/jxyJnSs +RMtMPPm5Jusg== From: Jeff Layton Date: Wed, 19 Nov 2025 08:42:20 -0500 Subject: [PATCH v8 3/3] filelock: allow lease_managers to dictate what qualifies as a conflict Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251119-dir-deleg-ro-v8-3-81b6cf5485c6@kernel.org> References: <20251119-dir-deleg-ro-v8-0-81b6cf5485c6@kernel.org> In-Reply-To: <20251119-dir-deleg-ro-v8-0-81b6cf5485c6@kernel.org> To: Alexander Viro , Christian Brauner , Jan Kara , Jeff Layton , Chuck Lever , Alexander Aring , NeilBrown , Olga Kornievskaia , Dai Ngo , Tom Talpey Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-nfs@vger.kernel.org, Stephen Rothwell X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=7247; i=jlayton@kernel.org; h=from:subject:message-id; bh=XmLiut+Qzx6ykbbfVHIfDiwFQuxqxqG6Krfa30IzFN4=; b=owEBbQKS/ZANAwAKAQAOaEEZVoIVAcsmYgBpHclPQggM3sdTM54h5jYcqsmAkQ6qIw7sZEwVF mO4Ub7/NbWJAjMEAAEKAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCaR3JTwAKCRAADmhBGVaC FZyXEADRUkJmbtI7QG0gD6bJL4wFOsX4H4mocZz+bx3YkpHDd9IfKJR54ePwf1DNFVTLfzHDq04 QbOdw8g824GdkgQTx48CUgYtwh/w4GfXQKnFmFIW/ljBtbwAZce6iwlz3Kcw7y3ysY1M7dqBTBk szEhWxWAhWhObUB0ujrEOk2w119WVyQU9I7RXpxDhbgmUYAfgDNakBhNwbo0gR0G9A/RLLuL7a4 95Rpa4T67udY9CPsAriDKbMBsfWun5zZB9EN2miBdw3lf2Kr7J2KZtv5jWNXVwWf1NMwPW3bnRA NF/Q+ZfPOYCdJQFxm0DsgGxLsKcYJfWmQg9l36efa6nLmDApVfwC95K4oYd3ZjZbd7TyZVoA37/ 8e2SAA8nw3KbSTpvWjbuCqUEtRoYvTj1P3jqf5z5/0ZMzy4p8GXQ9wd5qnqfHmnOdypEVwwe0w3 nQpG5vBoctqI7kkCxOMNVjjnDtjUcZlQjrLhZf4bBqCs0iei/9q3om/3aqIwwVmcfvIwIjERWqd sfYugGW8bwz3+pUYqlGPEI3F3hiXD/bOyc93EDctpfmHLkj/9lSREmGN9UobVAq2pBI8ARG2POC lEiJ+IUzd+W4rP2a0WD/MbsVOZSl1erv27Udzxq7Ij20I7JkW0qwURzQaaRh2AJzDqr6ybwwcd4 PfsUfhIrCPddWuA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Requesting a delegation on a file from the userland fcntl() interface currently succeeds when there are conflicting opens present. This is because the lease handling code ignores conflicting opens for FL_LAYOUT and FL_DELEG leases. This was a hack put in place long ago, because nfsd already checks for conflicts in its own way. The kernel needs to perform this check for userland delegations the same way it is done for leases, however. Make this dependent on the lease_manager by adding a new ->lm_open_conflict() lease_manager operation and have generic_add_lease() call that instead of check_conflicting_open(). Morph check_conflicting_open() into a ->lm_open_conflict() op that is only called for userland leases/delegations. Set the ->lm_open_conflict() operations for nfsd to trivial functions that always return 0. Signed-off-by: Jeff Layton --- fs/locks.c | 90 ++++++++++++++++++++++----------------------= ---- fs/nfsd/nfs4layouts.c | 11 ++++-- fs/nfsd/nfs4state.c | 7 ++++ include/linux/filelock.h | 1 + 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index d4e6af6ac625204b337e94fd1e4f6df2eee5cf50..62fbfce0407b77423e1591290cf= 57c4e2c5faeb4 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -585,10 +585,50 @@ lease_setup(struct file_lease *fl, void **priv) __f_setown(filp, task_pid(current), PIDTYPE_TGID, 0); } =20 +/** + * lease_open_conflict - see if the given file points to an inode that has + * an existing open that would conflict with the + * desired lease. + * @filp: file to check + * @arg: type of lease that we're trying to acquire + * + * Check to see if there's an existing open fd on this file that would + * conflict with the lease we're trying to set. + */ +static int +lease_open_conflict(struct file *filp, const int arg) +{ + struct inode *inode =3D file_inode(filp); + int self_wcount =3D 0, self_rcount =3D 0; + + if (arg =3D=3D F_RDLCK) + return inode_is_open_for_write(inode) ? -EAGAIN : 0; + else if (arg !=3D F_WRLCK) + return 0; + + /* + * Make sure that only read/write count is from lease requestor. + * Note that this will result in denying write leases when i_writecount + * is negative, which is what we want. (We shouldn't grant write leases + * on files open for execution.) + */ + if (filp->f_mode & FMODE_WRITE) + self_wcount =3D 1; + else if (filp->f_mode & FMODE_READ) + self_rcount =3D 1; + + if (atomic_read(&inode->i_writecount) !=3D self_wcount || + atomic_read(&inode->i_readcount) !=3D self_rcount) + return -EAGAIN; + + return 0; +} + static const struct lease_manager_operations lease_manager_ops =3D { .lm_break =3D lease_break_callback, .lm_change =3D lease_modify, .lm_setup =3D lease_setup, + .lm_open_conflict =3D lease_open_conflict, }; =20 /* @@ -1762,52 +1802,6 @@ int fcntl_getdeleg(struct file *filp, struct delegat= ion *deleg) return 0; } =20 -/** - * check_conflicting_open - see if the given file points to an inode that = has - * an existing open that would conflict with the - * desired lease. - * @filp: file to check - * @arg: type of lease that we're trying to acquire - * @flags: current lock flags - * - * Check to see if there's an existing open fd on this file that would - * conflict with the lease we're trying to set. - */ -static int -check_conflicting_open(struct file *filp, const int arg, int flags) -{ - struct inode *inode =3D file_inode(filp); - int self_wcount =3D 0, self_rcount =3D 0; - - if (flags & FL_LAYOUT) - return 0; - if (flags & FL_DELEG) - /* We leave these checks to the caller */ - return 0; - - if (arg =3D=3D F_RDLCK) - return inode_is_open_for_write(inode) ? -EAGAIN : 0; - else if (arg !=3D F_WRLCK) - return 0; - - /* - * Make sure that only read/write count is from lease requestor. - * Note that this will result in denying write leases when i_writecount - * is negative, which is what we want. (We shouldn't grant write leases - * on files open for execution.) - */ - if (filp->f_mode & FMODE_WRITE) - self_wcount =3D 1; - else if (filp->f_mode & FMODE_READ) - self_rcount =3D 1; - - if (atomic_read(&inode->i_writecount) !=3D self_wcount || - atomic_read(&inode->i_readcount) !=3D self_rcount) - return -EAGAIN; - - return 0; -} - static int generic_add_lease(struct file *filp, int arg, struct file_lease **flp, voi= d **priv) { @@ -1844,7 +1838,7 @@ generic_add_lease(struct file *filp, int arg, struct = file_lease **flp, void **pr percpu_down_read(&file_rwsem); spin_lock(&ctx->flc_lock); time_out_leases(inode, &dispose); - error =3D check_conflicting_open(filp, arg, lease->c.flc_flags); + error =3D lease->fl_lmops->lm_open_conflict(filp, arg); if (error) goto out; =20 @@ -1901,7 +1895,7 @@ generic_add_lease(struct file *filp, int arg, struct = file_lease **flp, void **pr * precedes these checks. */ smp_mb(); - error =3D check_conflicting_open(filp, arg, lease->c.flc_flags); + error =3D lease->fl_lmops->lm_open_conflict(filp, arg); if (error) { locks_unlink_lock_ctx(&lease->c); goto out; diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 683bd1130afe298f9df774684192c89f68102b72..ca7ec7a022bd5c12fad60ff9e51= 145d9cca55527 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -764,9 +764,16 @@ nfsd4_layout_lm_change(struct file_lease *onlist, int = arg, return lease_modify(onlist, arg, dispose); } =20 +static int +nfsd4_layout_lm_open_conflict(struct file *filp, int arg) +{ + return 0; +} + static const struct lease_manager_operations nfsd4_layouts_lm_ops =3D { - .lm_break =3D nfsd4_layout_lm_break, - .lm_change =3D nfsd4_layout_lm_change, + .lm_break =3D nfsd4_layout_lm_break, + .lm_change =3D nfsd4_layout_lm_change, + .lm_open_conflict =3D nfsd4_layout_lm_open_conflict, }; =20 int diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8f8c9385101e15b64883eabec71775f26b14f890..669fabb095407e61525e5b71268= cf1f06fc09877 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -5543,10 +5543,17 @@ nfsd_change_deleg_cb(struct file_lease *onlist, int= arg, return -EAGAIN; } =20 +static int +nfsd4_deleg_lm_open_conflict(struct file *filp, int arg) +{ + return 0; +} + static const struct lease_manager_operations nfsd_lease_mng_ops =3D { .lm_breaker_owns_lease =3D nfsd_breaker_owns_lease, .lm_break =3D nfsd_break_deleg_cb, .lm_change =3D nfsd_change_deleg_cb, + .lm_open_conflict =3D nfsd4_deleg_lm_open_conflict, }; =20 static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struc= t nfs4_stateowner *so, u32 seqid) diff --git a/include/linux/filelock.h b/include/linux/filelock.h index 54b824c05299261e6bd6acc4175cb277ea35b35d..2f5e5588ee0733c200103801d0d= 2ba19bebbf9af 100644 --- a/include/linux/filelock.h +++ b/include/linux/filelock.h @@ -49,6 +49,7 @@ struct lease_manager_operations { int (*lm_change)(struct file_lease *, int, struct list_head *); void (*lm_setup)(struct file_lease *, void **); bool (*lm_breaker_owns_lease)(struct file_lease *); + int (*lm_open_conflict)(struct file *, int); }; =20 struct lock_manager { --=20 2.51.1