diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index ce23924f01b3..6b97f7a91235 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -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, diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index c455b15f2778..c35074ce5aad 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -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); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 82e0adc1dabd..943b7cd2c096 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -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 */ diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b60344125f27..58430ba51b10 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -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);