mirror of
https://github.com/torvalds/linux.git
synced 2026-07-30 03:01:41 +02:00
smb/client: update i_blocks after contiguous writes
When a lease allows CIFS to use cached inode attributes, getattr may
return the locally cached attributes instead of revalidating them from
the server. After local writes extend a file, the write path updates the
file size, but i_blocks can remain based on the old allocation size.
For example, while the file is still open after two contiguous writes,
the local block count can remain smaller than the written range:
after first write: st_size = 4096, st_blocks = 7
after second write: st_size = 12288, st_blocks = 21
after close: st_size = 12288, st_blocks = 24
This can make a fully written file look sparse:
i_blocks * 512 < i_size
and can cause swap activation to reject a valid write-created swapfile
as having holes. This results in xfstests skipping swap-related tests
on CIFS mounts:
generic/472 [not run] swapfiles are not supported
generic/494 [not run] swapfiles are not supported
generic/497 [not run] swapfiles are not supported
generic/569 [not run] swapfiles are not supported
generic/636 [not run] swapfiles are not supported
generic/643 [not run] swapfiles are not supported
Update the local i_blocks estimate after successful writes, but only
when the write starts at or before the currently known allocated range.
This lets sequential writes grow i_blocks while avoiding treating
write-past-EOF holes as allocated.
Skip the local estimate for files that are already marked sparse, since
their allocation needs to come from the server rather than from a
contiguous-write estimate.
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
e8a5cf2ff5
commit
af25ab681e
|
|
@ -1615,7 +1615,7 @@ const struct file_operations cifs_file_strict_ops = {
|
|||
|
||||
const struct file_operations cifs_file_direct_ops = {
|
||||
.read_iter = netfs_unbuffered_read_iter,
|
||||
.write_iter = netfs_file_write_iter,
|
||||
.write_iter = cifs_direct_write_iter,
|
||||
.open = cifs_open,
|
||||
.release = cifs_close,
|
||||
.lock = cifs_lock,
|
||||
|
|
@ -1671,7 +1671,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
|
|||
|
||||
const struct file_operations cifs_file_direct_nobrl_ops = {
|
||||
.read_iter = netfs_unbuffered_read_iter,
|
||||
.write_iter = netfs_file_write_iter,
|
||||
.write_iter = cifs_direct_write_iter,
|
||||
.open = cifs_open,
|
||||
.release = cifs_close,
|
||||
.fsync = cifs_fsync,
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ int cifs_closedir(struct inode *inode, struct file *file);
|
|||
ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to);
|
||||
ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);
|
||||
ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
||||
ssize_t cifs_direct_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
||||
ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter);
|
||||
int cifs_flock(struct file *file, int cmd, struct file_lock *fl);
|
||||
int cifs_lock(struct file *file, int cmd, struct file_lock *flock);
|
||||
|
|
|
|||
|
|
@ -2387,9 +2387,12 @@ static inline int cifs_open_create_options(unsigned int oflags, int opts)
|
|||
}
|
||||
|
||||
/*
|
||||
* The number of blocks is not related to (i_size / i_blksize), but instead
|
||||
* 512 byte (2**9) size is required for calculating num blocks.
|
||||
* inode->i_blocks is counted in 512-byte units, independent of
|
||||
* inode->i_blksize.
|
||||
*/
|
||||
#define CIFS_INO_BLOCKS(size) DIV_ROUND_UP_ULL((u64)(size), 512)
|
||||
#define CIFS_INO_BLOCK_SIZE 512ULL
|
||||
#define CIFS_INO_BLOCKS(size) \
|
||||
DIV_ROUND_UP_ULL((u64)(size), CIFS_INO_BLOCK_SIZE)
|
||||
#define CIFS_INO_BYTES(blocks) ((u64)(blocks) * CIFS_INO_BLOCK_SIZE)
|
||||
|
||||
#endif /* _CIFS_GLOB_H */
|
||||
|
|
|
|||
|
|
@ -2514,6 +2514,42 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void cifs_update_i_blocks_for_write(struct inode *inode, loff_t start,
|
||||
loff_t end)
|
||||
{
|
||||
struct cifsInodeInfo *cinode = CIFS_I(inode);
|
||||
u64 allocated_end = CIFS_INO_BYTES(inode->i_blocks);
|
||||
u64 blocks;
|
||||
|
||||
if (cinode->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Grow the local estimate only across the currently known allocated
|
||||
* prefix. A write beyond that may leave a hole.
|
||||
*/
|
||||
if ((u64)start > allocated_end)
|
||||
return;
|
||||
|
||||
blocks = CIFS_INO_BLOCKS(end);
|
||||
if ((u64)inode->i_blocks < blocks)
|
||||
inode->i_blocks = blocks;
|
||||
}
|
||||
|
||||
static void cifs_update_i_blocks_after_write(struct kiocb *iocb,
|
||||
ssize_t written)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
loff_t end = iocb->ki_pos;
|
||||
|
||||
if (written <= 0)
|
||||
return;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
cifs_update_i_blocks_for_write(inode, end - written, end);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result)
|
||||
{
|
||||
struct netfs_io_request *wreq = wdata->rreq;
|
||||
|
|
@ -2532,6 +2568,8 @@ void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t
|
|||
netfs_write_zero_point(inode, wrend);
|
||||
if (wrend > ictx->_remote_i_size)
|
||||
netfs_resize_file(ictx, wrend, true);
|
||||
cifs_update_i_blocks_for_write(inode, wdata->subreq.start,
|
||||
wrend);
|
||||
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
|
@ -2920,6 +2958,7 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
}
|
||||
|
||||
rc = netfs_buffered_write_iter_locked(iocb, from, NULL);
|
||||
cifs_update_i_blocks_after_write(iocb, rc);
|
||||
|
||||
out:
|
||||
up_read(&cinode->lock_sem);
|
||||
|
|
@ -2949,6 +2988,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
|
||||
((cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NOPOSIXBRL) == 0)) {
|
||||
written = netfs_file_write_iter(iocb, from);
|
||||
cifs_update_i_blocks_after_write(iocb, written);
|
||||
goto out;
|
||||
}
|
||||
written = cifs_writev(iocb, from);
|
||||
|
|
@ -2961,6 +3001,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
* these pages but not on the region from pos to ppos+len-1.
|
||||
*/
|
||||
written = netfs_file_write_iter(iocb, from);
|
||||
cifs_update_i_blocks_after_write(iocb, written);
|
||||
if (CIFS_CACHE_READ(cinode)) {
|
||||
/*
|
||||
* We have read level caching and we have just sent a write
|
||||
|
|
@ -2979,6 +3020,15 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
return written;
|
||||
}
|
||||
|
||||
ssize_t cifs_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
ssize_t written;
|
||||
|
||||
written = netfs_file_write_iter(iocb, from);
|
||||
cifs_update_i_blocks_after_write(iocb, written);
|
||||
return written;
|
||||
}
|
||||
|
||||
ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
{
|
||||
ssize_t rc;
|
||||
|
|
@ -3003,6 +3053,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
|
||||
if (iocb->ki_filp->f_flags & O_DIRECT) {
|
||||
written = netfs_unbuffered_write_iter(iocb, from);
|
||||
cifs_update_i_blocks_after_write(iocb, written);
|
||||
if (written > 0 && CIFS_CACHE_READ(cinode)) {
|
||||
cifs_zap_mapping(inode);
|
||||
cifs_dbg(FYI,
|
||||
|
|
@ -3018,6 +3069,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
return written;
|
||||
|
||||
written = netfs_file_write_iter(iocb, from);
|
||||
cifs_update_i_blocks_after_write(iocb, written);
|
||||
|
||||
if (!CIFS_CACHE_WRITE(CIFS_I(inode))) {
|
||||
rc = filemap_fdatawrite(inode->i_mapping);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user