Skip to content

Commit 5cf3c59

Browse files
committed
fuse: take inode lock shared for cached writes
Signed-off-by: Horst Birthelmer <hbirthelmer@ddn.com>
1 parent 2493f33 commit 5cf3c59

1 file changed

Lines changed: 105 additions & 5 deletions

File tree

fs/fuse/file.c

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,44 @@ static bool fuse_use_writeback_cache(struct fuse_conn *fc, struct kiocb *iocb,
17681768
return ret;
17691769
}
17701770

1771+
/*
1772+
* @return true if an exclusive lock is needed for a cached (buffered) write.
1773+
*
1774+
* Buffered writes normally take the inode lock exclusively, serialising every
1775+
* writer to the same inode even when they target disjoint regions. When the
1776+
* connection uses the DLM (fc->dlm) and writeback caching is in effect for the
1777+
* write, the server is the authority that serialises writers across the
1778+
* cluster (the cached data carries a DLM write lock; the synchronous
1779+
* writethrough path above sends data straight to the server). The inode rwsem
1780+
* then no longer has to provide that exclusion - it only needs to keep i_size
1781+
* and the file-modified bookkeeping stable. Such writes may therefore share
1782+
* the inode lock, letting multiple writers to disjoint regions of one file
1783+
* proceed concurrently (e.g. MPI-IO / IOR shared-file workloads).
1784+
*/
1785+
static bool fuse_cache_wr_exclusive_lock(struct kiocb *iocb, bool cache_mode)
1786+
{
1787+
struct inode *inode = file_inode(iocb->ki_filp);
1788+
struct fuse_conn *fc = get_fuse_conn(inode);
1789+
1790+
/*
1791+
* Without the DLM the inode rwsem is the only writer exclusion, and
1792+
* outside writeback-cache mode the write is covered by neither the DLM
1793+
* write lock nor the synchronous-server path described above.
1794+
*/
1795+
if (!fc->dlm || !cache_mode)
1796+
return true;
1797+
1798+
/* O_DIRECT writes fall back to generic_file_direct_write(). */
1799+
if (iocb->ki_flags & IOCB_DIRECT)
1800+
return true;
1801+
1802+
/* Append needs the eventual EOF - always needs an exclusive lock. */
1803+
if (iocb->ki_flags & IOCB_APPEND)
1804+
return true;
1805+
1806+
return false;
1807+
}
1808+
17711809
static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from);
17721810

17731811
static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
@@ -1779,7 +1817,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
17791817
struct inode *inode = mapping->host;
17801818
ssize_t err, count;
17811819
struct fuse_conn *fc = get_fuse_conn(inode);
1820+
struct fuse_inode *fi = get_fuse_inode(inode);
17821821
bool writeback = false;
1822+
bool cache_mode = false;
1823+
bool exclusive;
17831824

17841825
/*
17851826
* The inode may have been latched into forced direct IO after this
@@ -1797,9 +1838,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
17971838
if (err)
17981839
return err;
17991840

1800-
if ((!fc->handle_killpriv_v2 ||
1801-
!setattr_should_drop_suidgid(idmap, file_inode(file))) &&
1802-
fuse_use_writeback_cache(fc, iocb, from)) {
1841+
cache_mode = !fc->handle_killpriv_v2 ||
1842+
!setattr_should_drop_suidgid(idmap, file_inode(file));
1843+
1844+
if (cache_mode && fuse_use_writeback_cache(fc, iocb, from)) {
18031845
writeback = true;
18041846

18051847
/*
@@ -1827,7 +1869,11 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
18271869
}
18281870
}
18291871

1830-
inode_lock(inode);
1872+
exclusive = fuse_cache_wr_exclusive_lock(iocb, cache_mode);
1873+
if (exclusive)
1874+
inode_lock(inode);
1875+
else
1876+
inode_lock_shared(inode);
18311877

18321878
err = count = generic_write_checks(iocb, from);
18331879
if (err <= 0)
@@ -1846,7 +1892,58 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
18461892
written = direct_write_fallback(iocb, from, written,
18471893
fuse_perform_write(iocb, from));
18481894
} else if (writeback) {
1895+
loff_t pos = iocb->ki_pos;
1896+
loff_t end = pos + count;
1897+
loff_t orig_size = 0;
1898+
bool extended = false;
1899+
1900+
if (fc->dlm && !exclusive && end > i_size_read(inode)) {
1901+
/*
1902+
* Lockless pre-check above keeps in-bounds writes off
1903+
* fi->lock; under the shared inode lock i_size only grows
1904+
* (extenders take fi->lock, truncate is excluded), so a
1905+
* stale read can only over-trigger this slow path, never
1906+
* miss an extension. Re-check authoritatively here.
1907+
*
1908+
* Pre-growing under fi->lock is required: iomap's own
1909+
* i_size update (iomap_write_iter) is a non-atomic
1910+
* read-modify-write done under only the folio lock, so
1911+
* concurrent shared extenders would lose updates. Once
1912+
* i_size already covers our range iomap's update becomes
1913+
* a no-op and all growth stays serialised on fi->lock.
1914+
*/
1915+
spin_lock(&fi->lock);
1916+
orig_size = i_size_read(inode);
1917+
if (end > orig_size) {
1918+
i_size_write(inode, end);
1919+
extended = true;
1920+
}
1921+
spin_unlock(&fi->lock);
1922+
1923+
/* Zero the tail of the folio straddling the old EOF. */
1924+
if (extended && orig_size < pos)
1925+
pagecache_isize_extended(inode, orig_size, pos);
1926+
}
1927+
18491928
written = fuse_writeback_write_iter(iocb, from, file);
1929+
1930+
/*
1931+
* Reconcile the speculative extension with what was actually
1932+
* written. Only retract the tail if no concurrent extender has
1933+
* pushed i_size past our claim; otherwise [reached, end) is a
1934+
* legitimate hole inside their extension and must remain.
1935+
*/
1936+
if (extended) {
1937+
loff_t reached = written > 0 ? pos + written : orig_size;
1938+
1939+
if (reached < end) {
1940+
spin_lock(&fi->lock);
1941+
if (i_size_read(inode) == end)
1942+
i_size_write(inode, reached);
1943+
spin_unlock(&fi->lock);
1944+
}
1945+
}
1946+
18501947
if (written < 0) {
18511948
err = written;
18521949
goto out;
@@ -1855,7 +1952,10 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
18551952
written = fuse_perform_write(iocb, from);
18561953
}
18571954
out:
1858-
inode_unlock(inode);
1955+
if (exclusive)
1956+
inode_unlock(inode);
1957+
else
1958+
inode_unlock_shared(inode);
18591959
if (written > 0)
18601960
written = generic_write_sync(iocb, written);
18611961

0 commit comments

Comments
 (0)