Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge patch series "filelock: fix conflict detection with userland file delegations"

Jeff Layton <jlayton@kernel.org> says:

This patchset fixes the way that conflicts are detected when userland
requests file delegations. The problem is due to a hack that was added
long ago which worked up until userland could request a file delegation.

This fixes the bug and makes things a bit less hacky. Please consider
for v6.19.

* patches from https://patch.msgid.link/20251204-dir-deleg-ro-v2-0-22d37f92ce2c@kernel.org:
filelock: allow lease_managers to dictate what qualifies as a conflict
filelock: add lease_dispose_list() helper

Link: https://patch.msgid.link/20251204-dir-deleg-ro-v2-0-22d37f92ce2c@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>

+103 -60
+1
Documentation/filesystems/locking.rst
··· 416 416 lm_breaker_owns_lease: yes no no 417 417 lm_lock_expirable yes no no 418 418 lm_expire_lock no no yes 419 + lm_open_conflict yes no no 419 420 ====================== ============= ================= ========= 420 421 421 422 buffer_head
+61 -58
fs/locks.c
··· 369 369 while (!list_empty(dispose)) { 370 370 flc = list_first_entry(dispose, struct file_lock_core, flc_list); 371 371 list_del_init(&flc->flc_list); 372 - if (flc->flc_flags & (FL_LEASE|FL_DELEG|FL_LAYOUT)) 373 - locks_free_lease(file_lease(flc)); 374 - else 375 - locks_free_lock(file_lock(flc)); 372 + locks_free_lock(file_lock(flc)); 373 + } 374 + } 375 + 376 + static void 377 + lease_dispose_list(struct list_head *dispose) 378 + { 379 + struct file_lock_core *flc; 380 + 381 + while (!list_empty(dispose)) { 382 + flc = list_first_entry(dispose, struct file_lock_core, flc_list); 383 + list_del_init(&flc->flc_list); 384 + locks_free_lease(file_lease(flc)); 376 385 } 377 386 } 378 387 ··· 585 576 __f_setown(filp, task_pid(current), PIDTYPE_TGID, 0); 586 577 } 587 578 579 + /** 580 + * lease_open_conflict - see if the given file points to an inode that has 581 + * an existing open that would conflict with the 582 + * desired lease. 583 + * @filp: file to check 584 + * @arg: type of lease that we're trying to acquire 585 + * 586 + * Check to see if there's an existing open fd on this file that would 587 + * conflict with the lease we're trying to set. 588 + */ 589 + static int 590 + lease_open_conflict(struct file *filp, const int arg) 591 + { 592 + struct inode *inode = file_inode(filp); 593 + int self_wcount = 0, self_rcount = 0; 594 + 595 + if (arg == F_RDLCK) 596 + return inode_is_open_for_write(inode) ? -EAGAIN : 0; 597 + else if (arg != F_WRLCK) 598 + return 0; 599 + 600 + /* 601 + * Make sure that only read/write count is from lease requestor. 602 + * Note that this will result in denying write leases when i_writecount 603 + * is negative, which is what we want. (We shouldn't grant write leases 604 + * on files open for execution.) 605 + */ 606 + if (filp->f_mode & FMODE_WRITE) 607 + self_wcount = 1; 608 + else if (filp->f_mode & FMODE_READ) 609 + self_rcount = 1; 610 + 611 + if (atomic_read(&inode->i_writecount) != self_wcount || 612 + atomic_read(&inode->i_readcount) != self_rcount) 613 + return -EAGAIN; 614 + 615 + return 0; 616 + } 617 + 588 618 static const struct lease_manager_operations lease_manager_ops = { 589 619 .lm_break = lease_break_callback, 590 620 .lm_change = lease_modify, 591 621 .lm_setup = lease_setup, 622 + .lm_open_conflict = lease_open_conflict, 592 623 }; 593 624 594 625 /* ··· 1669 1620 spin_unlock(&ctx->flc_lock); 1670 1621 percpu_up_read(&file_rwsem); 1671 1622 1672 - locks_dispose_list(&dispose); 1623 + lease_dispose_list(&dispose); 1673 1624 error = wait_event_interruptible_timeout(new_fl->c.flc_wait, 1674 1625 list_empty(&new_fl->c.flc_blocked_member), 1675 1626 break_time); ··· 1692 1643 out: 1693 1644 spin_unlock(&ctx->flc_lock); 1694 1645 percpu_up_read(&file_rwsem); 1695 - locks_dispose_list(&dispose); 1646 + lease_dispose_list(&dispose); 1696 1647 free_lock: 1697 1648 locks_free_lease(new_fl); 1698 1649 return error; ··· 1776 1727 spin_unlock(&ctx->flc_lock); 1777 1728 percpu_up_read(&file_rwsem); 1778 1729 1779 - locks_dispose_list(&dispose); 1730 + lease_dispose_list(&dispose); 1780 1731 } 1781 1732 return type; 1782 1733 } ··· 1791 1742 if (deleg->d_flags != 0 || deleg->__pad != 0) 1792 1743 return -EINVAL; 1793 1744 deleg->d_type = __fcntl_getlease(filp, FL_DELEG); 1794 - return 0; 1795 - } 1796 - 1797 - /** 1798 - * check_conflicting_open - see if the given file points to an inode that has 1799 - * an existing open that would conflict with the 1800 - * desired lease. 1801 - * @filp: file to check 1802 - * @arg: type of lease that we're trying to acquire 1803 - * @flags: current lock flags 1804 - * 1805 - * Check to see if there's an existing open fd on this file that would 1806 - * conflict with the lease we're trying to set. 1807 - */ 1808 - static int 1809 - check_conflicting_open(struct file *filp, const int arg, int flags) 1810 - { 1811 - struct inode *inode = file_inode(filp); 1812 - int self_wcount = 0, self_rcount = 0; 1813 - 1814 - if (flags & FL_LAYOUT) 1815 - return 0; 1816 - if (flags & FL_DELEG) 1817 - /* We leave these checks to the caller */ 1818 - return 0; 1819 - 1820 - if (arg == F_RDLCK) 1821 - return inode_is_open_for_write(inode) ? -EAGAIN : 0; 1822 - else if (arg != F_WRLCK) 1823 - return 0; 1824 - 1825 - /* 1826 - * Make sure that only read/write count is from lease requestor. 1827 - * Note that this will result in denying write leases when i_writecount 1828 - * is negative, which is what we want. (We shouldn't grant write leases 1829 - * on files open for execution.) 1830 - */ 1831 - if (filp->f_mode & FMODE_WRITE) 1832 - self_wcount = 1; 1833 - else if (filp->f_mode & FMODE_READ) 1834 - self_rcount = 1; 1835 - 1836 - if (atomic_read(&inode->i_writecount) != self_wcount || 1837 - atomic_read(&inode->i_readcount) != self_rcount) 1838 - return -EAGAIN; 1839 - 1840 1745 return 0; 1841 1746 } 1842 1747 ··· 1830 1827 percpu_down_read(&file_rwsem); 1831 1828 spin_lock(&ctx->flc_lock); 1832 1829 time_out_leases(inode, &dispose); 1833 - error = check_conflicting_open(filp, arg, lease->c.flc_flags); 1830 + error = lease->fl_lmops->lm_open_conflict(filp, arg); 1834 1831 if (error) 1835 1832 goto out; 1836 1833 ··· 1887 1884 * precedes these checks. 1888 1885 */ 1889 1886 smp_mb(); 1890 - error = check_conflicting_open(filp, arg, lease->c.flc_flags); 1887 + error = lease->fl_lmops->lm_open_conflict(filp, arg); 1891 1888 if (error) { 1892 1889 locks_unlink_lock_ctx(&lease->c); 1893 1890 goto out; ··· 1899 1896 out: 1900 1897 spin_unlock(&ctx->flc_lock); 1901 1898 percpu_up_read(&file_rwsem); 1902 - locks_dispose_list(&dispose); 1899 + lease_dispose_list(&dispose); 1903 1900 if (is_deleg) 1904 1901 inode_unlock(inode); 1905 1902 if (!error && !my_fl) ··· 1935 1932 error = fl->fl_lmops->lm_change(victim, F_UNLCK, &dispose); 1936 1933 spin_unlock(&ctx->flc_lock); 1937 1934 percpu_up_read(&file_rwsem); 1938 - locks_dispose_list(&dispose); 1935 + lease_dispose_list(&dispose); 1939 1936 return error; 1940 1937 } 1941 1938 ··· 2730 2727 spin_unlock(&ctx->flc_lock); 2731 2728 percpu_up_read(&file_rwsem); 2732 2729 2733 - locks_dispose_list(&dispose); 2730 + lease_dispose_list(&dispose); 2734 2731 } 2735 2732 2736 2733 /*
+21 -2
fs/nfsd/nfs4layouts.c
··· 764 764 return lease_modify(onlist, arg, dispose); 765 765 } 766 766 767 + /** 768 + * nfsd4_layout_lm_open_conflict - see if the given file points to an inode that has 769 + * an existing open that would conflict with the 770 + * desired lease. 771 + * @filp: file to check 772 + * @arg: type of lease that we're trying to acquire 773 + * 774 + * The kernel will call into this operation to determine whether there 775 + * are conflicting opens that may prevent the layout from being granted. 776 + * For nfsd, that check is done at a higher level, so this trivially 777 + * returns 0. 778 + */ 779 + static int 780 + nfsd4_layout_lm_open_conflict(struct file *filp, int arg) 781 + { 782 + return 0; 783 + } 784 + 767 785 static const struct lease_manager_operations nfsd4_layouts_lm_ops = { 768 - .lm_break = nfsd4_layout_lm_break, 769 - .lm_change = nfsd4_layout_lm_change, 786 + .lm_break = nfsd4_layout_lm_break, 787 + .lm_change = nfsd4_layout_lm_change, 788 + .lm_open_conflict = nfsd4_layout_lm_open_conflict, 770 789 }; 771 790 772 791 int
+19
fs/nfsd/nfs4state.c
··· 5552 5552 return -EAGAIN; 5553 5553 } 5554 5554 5555 + /** 5556 + * nfsd4_deleg_lm_open_conflict - see if the given file points to an inode that has 5557 + * an existing open that would conflict with the 5558 + * desired lease. 5559 + * @filp: file to check 5560 + * @arg: type of lease that we're trying to acquire 5561 + * 5562 + * The kernel will call into this operation to determine whether there 5563 + * are conflicting opens that may prevent the deleg from being granted. 5564 + * For nfsd, that check is done at a higher level, so this trivially 5565 + * returns 0. 5566 + */ 5567 + static int 5568 + nfsd4_deleg_lm_open_conflict(struct file *filp, int arg) 5569 + { 5570 + return 0; 5571 + } 5572 + 5555 5573 static const struct lease_manager_operations nfsd_lease_mng_ops = { 5556 5574 .lm_breaker_owns_lease = nfsd_breaker_owns_lease, 5557 5575 .lm_break = nfsd_break_deleg_cb, 5558 5576 .lm_change = nfsd_change_deleg_cb, 5577 + .lm_open_conflict = nfsd4_deleg_lm_open_conflict, 5559 5578 }; 5560 5579 5561 5580 static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4_stateowner *so, u32 seqid)
+1
include/linux/filelock.h
··· 49 49 int (*lm_change)(struct file_lease *, int, struct list_head *); 50 50 void (*lm_setup)(struct file_lease *, void **); 51 51 bool (*lm_breaker_owns_lease)(struct file_lease *); 52 + int (*lm_open_conflict)(struct file *, int); 52 53 }; 53 54 54 55 struct lock_manager {