mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Merge patch series "netfs: Miscellaneous fixes"
David Howells <dhowells@redhat.com> says:
Here are some miscellaneous fixes for netfslib. I separated them from my
netfs-next branch. Various Sashiko review comments[1][2][3] are addressed:
(1) Fix the decision whether to disallow write-streaming due to fscache
use.
(2) Fix netfs_create_write_req() to better handle async cache object
creation.
(3) Fix a double fput in cachefiles_create_tmpfile().
(4) Fix alteration of S_KERNEL_FILE inode flag without holding inode lock.
(5) Fix a potential mathematical underflow in
iov_iter_extract_xarray_pages() and make it return 0 and free the
array if no pages could be extracted.
(6) Fix a missing alloc failure check in iov_iter_extract_bvec_pages().
(7) Fix iov_iter_extract_user_pages() so that it doesn't leak the pages
array if it returns an error or 0 (inasmuch as the leak is really in
the callers).
(8) Remove an unused variable in kunit_iov_iter.c.
(9) Fix extract_xarray_to_sg() to calculate folio offset correctly.
(10) Fix a kdoc comment.
(11) Replace the netfs_inode::wb_lock mutex with a bit lock so that the
lock can be passed to the collector so that multiple asynchronous
writebacks won't interfere with each other.
(12) Fix writeback error handling to go through writeback_iter() so that it
can clean up its state.
(13) Fix ENOMEM handling in writeback to clean up the current folio if we
can't allocate a rolling buffer segment.
(14) Fix unbuffered/DIO write retry for filesystems that don't have a
->prepare_write() method.
[1] https://sashiko.dev/#/patchset/20260608145432.681865-1-dhowells%40redhat.com
[2] https://sashiko.dev/#/patchset/20260616100821.2062304-1-dhowells%40redhat.com
[3] https://sashiko.dev/#/patchset/20260619140646.2633762-1-dhowells%40redhat.com
[4] https://sashiko.dev/#/patchset/20260624115737.2964520-1-dhowells%40redhat.com
* patches from https://patch.msgid.link/20260625140640.3116900-1-dhowells@redhat.com:
netfs: Fix DIO write retry for filesystems without a ->prepare_write()
netfs: Fix folio state after ENOMEM whilst under writeback iteration
netfs: Fix writeback error handling
netfs: Fix writethrough to use collection offload
netfs: Replace wb_lock with a bit lock for asynchronicity
netfs: Fix kdoc warning
scatterlist: Fix offset in folio calc in extract_xarray_to_sg()
iov_iter: Remove unused variable in kunit_iov_iter.c
iov_iter: Fix a memory leak in iov_iter_extract_user_pages()
iov_iter: Fix missing alloc fail check in iov_iter_extract_bvec_pages()
iov_iter: Fix potential underflow in iov_iter_extract_xarray_pages()
cachefiles: Fix file burial to take lock when unsetting S_KERNEL_FILE
cachefiles: Fix double fput
netfs: Fix netfs_create_write_req() to handle async cache object creation
netfs: Fix decision whether to disallow write-streaming due to fscache use
Link: https://patch.msgid.link/20260625140640.3116900-1-dhowells@redhat.com
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
commit
e0df90e4c6
|
|
@ -255,11 +255,11 @@ int afs_symlink_writepages(struct address_space *mapping,
|
|||
}
|
||||
|
||||
if (ret == 0) {
|
||||
mutex_lock(&vnode->netfs.wb_lock);
|
||||
netfs_wb_begin(&vnode->netfs, false);
|
||||
netfs_free_folioq_buffer(vnode->directory);
|
||||
vnode->directory = NULL;
|
||||
vnode->directory_size = 0;
|
||||
mutex_unlock(&vnode->netfs.wb_lock);
|
||||
netfs_wb_end(&vnode->netfs);
|
||||
} else if (ret == 1) {
|
||||
ret = 0; /* Skipped write due to lock conflict. */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
|
|||
"Rename failed with error %d", ret);
|
||||
}
|
||||
|
||||
__cachefiles_unmark_inode_in_use(object, d_inode(rep));
|
||||
cachefiles_do_unmark_inode_in_use(object, d_inode(rep));
|
||||
end_renaming(&rd);
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
|
@ -466,7 +466,6 @@ struct file *cachefiles_create_tmpfile(struct cachefiles_object *object)
|
|||
ret = -EINVAL;
|
||||
if (unlikely(!file->f_op->read_iter) ||
|
||||
unlikely(!file->f_op->write_iter)) {
|
||||
fput(file);
|
||||
pr_notice("Cache does not support read_iter and write_iter\n");
|
||||
goto err_unuse;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -659,7 +659,7 @@ int netfs_write_begin(struct netfs_inode *ctx,
|
|||
* within the cache granule containing the EOF, in which case we need
|
||||
* to preload the granule.
|
||||
*/
|
||||
if (!netfs_is_cache_enabled(ctx) &&
|
||||
if (!netfs_is_cache_maybe_enabled(ctx) &&
|
||||
netfs_skip_folio_read(folio, pos, len, false)) {
|
||||
netfs_stat(&netfs_n_rh_write_zskip);
|
||||
goto have_folio_no_wait;
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
|
|||
* caching service temporarily because the backing store got
|
||||
* culled.
|
||||
*/
|
||||
if (netfs_is_cache_enabled(ctx)) {
|
||||
if (netfs_is_cache_maybe_enabled(ctx)) {
|
||||
if (finfo) {
|
||||
netfs_stat(&netfs_n_wh_wstream_conflict);
|
||||
goto flush_content;
|
||||
|
|
|
|||
|
|
@ -166,13 +166,16 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq)
|
|||
*/
|
||||
subreq->error = -EAGAIN;
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_retry);
|
||||
if (subreq->transferred > 0)
|
||||
if (subreq->transferred > 0) {
|
||||
iov_iter_advance(&wreq->buffer.iter, subreq->transferred);
|
||||
wreq->transferred += subreq->transferred;
|
||||
}
|
||||
|
||||
if (stream->source == NETFS_UPLOAD_TO_SERVER &&
|
||||
wreq->netfs_ops->retry_request)
|
||||
wreq->netfs_ops->retry_request(wreq, stream);
|
||||
|
||||
__clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
|
||||
__clear_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
|
||||
__clear_bit(NETFS_SREQ_BOUNDARY, &subreq->flags);
|
||||
__clear_bit(NETFS_SREQ_FAILED, &subreq->flags);
|
||||
|
|
@ -186,17 +189,10 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq)
|
|||
|
||||
netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
|
||||
|
||||
if (stream->prepare_write) {
|
||||
if (stream->prepare_write)
|
||||
stream->prepare_write(subreq);
|
||||
__set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags);
|
||||
netfs_stat(&netfs_n_wh_retry_write_subreq);
|
||||
} else {
|
||||
struct iov_iter source;
|
||||
|
||||
netfs_reset_iter(subreq);
|
||||
source = subreq->io_iter;
|
||||
netfs_reissue_write(stream, subreq, &source);
|
||||
}
|
||||
__set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags);
|
||||
netfs_stat(&netfs_n_wh_retry_write_subreq);
|
||||
}
|
||||
|
||||
netfs_unbuffered_write_done(wreq);
|
||||
|
|
|
|||
|
|
@ -239,6 +239,18 @@ static inline bool netfs_is_cache_enabled(struct netfs_inode *ctx)
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline bool netfs_is_cache_maybe_enabled(struct netfs_inode *ctx)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_FSCACHE)
|
||||
struct fscache_cookie *cookie = ctx->cache;
|
||||
|
||||
return fscache_cookie_valid(cookie) &&
|
||||
test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a ref on a netfs group attached to a dirty page (e.g. a ceph snap).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@
|
|||
#include <linux/netfs.h>
|
||||
#include "internal.h"
|
||||
|
||||
struct netfs_wb_waiter {
|
||||
struct list_head link; /* Link in ictx->wb_queue */
|
||||
struct task_struct *waiter; /* Waiter task; cleared when lock granted */
|
||||
};
|
||||
|
||||
/*
|
||||
* inode_dio_wait_interruptible - wait for outstanding DIO requests to finish
|
||||
* @inode: inode to wait for
|
||||
|
|
@ -203,3 +208,93 @@ void netfs_end_io_direct(struct inode *inode)
|
|||
up_read(&inode->i_rwsem);
|
||||
}
|
||||
EXPORT_SYMBOL(netfs_end_io_direct);
|
||||
|
||||
/*
|
||||
* Wait to have exclusive access to writeback.
|
||||
*/
|
||||
static bool netfs_wb_begin_wait(struct netfs_inode *ictx)
|
||||
{
|
||||
struct netfs_wb_waiter waiter = {};
|
||||
struct task_struct *tsk = current;
|
||||
bool got = false;
|
||||
|
||||
spin_lock(&ictx->lock);
|
||||
|
||||
if (test_and_set_bit_lock(NETFS_ICTX_WB_LOCK, &ictx->flags)) {
|
||||
get_task_struct(tsk);
|
||||
waiter.waiter = tsk;
|
||||
list_add_tail(&waiter.link, &ictx->wb_queue);
|
||||
} else {
|
||||
got = true;
|
||||
}
|
||||
spin_unlock(&ictx->lock);
|
||||
|
||||
if (!got) {
|
||||
for (;;) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
/* Read waiter before accessing inode state. */
|
||||
if (smp_load_acquire(&waiter.waiter) == NULL)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* netfs_wb_begin - Begin writeback, waiting if need be
|
||||
* @ictx: The inode to get writeback access on
|
||||
* @nowait: Return failure immediately rather than waiting if true
|
||||
*
|
||||
* Begin writeback to an inode, waiting for exclusive access if @nowait is
|
||||
* false. This prevents collection from being done out of order with respect
|
||||
* to the issuance of write subrequests.
|
||||
*
|
||||
* Note that writeback may be ended in a different process (e.g. the collection
|
||||
* function on a workqueue) than started it.
|
||||
*
|
||||
* Return: True if can proceed, false if denied.
|
||||
*/
|
||||
bool netfs_wb_begin(struct netfs_inode *ictx, bool nowait)
|
||||
{
|
||||
if (!test_and_set_bit_lock(NETFS_ICTX_WB_LOCK, &ictx->flags))
|
||||
return true;
|
||||
if (nowait) {
|
||||
netfs_stat(&netfs_n_wb_lock_skip);
|
||||
return false;
|
||||
}
|
||||
netfs_stat(&netfs_n_wb_lock_wait);
|
||||
return netfs_wb_begin_wait(ictx);
|
||||
}
|
||||
EXPORT_SYMBOL(netfs_wb_begin);
|
||||
|
||||
/* netfs_wb_end - End writeback
|
||||
* @ictx: The inode we have writeback access to
|
||||
*
|
||||
* End writeback access on an inode, waking up the next writeback request.
|
||||
*/
|
||||
void netfs_wb_end(struct netfs_inode *ictx)
|
||||
{
|
||||
struct netfs_wb_waiter *waiter;
|
||||
struct task_struct *tsk;
|
||||
|
||||
WARN_ON_ONCE(!test_bit(NETFS_ICTX_WB_LOCK, &ictx->flags));
|
||||
|
||||
spin_lock(&ictx->lock);
|
||||
|
||||
waiter = list_first_entry_or_null(&ictx->wb_queue, struct netfs_wb_waiter, link);
|
||||
if (waiter) {
|
||||
list_del(&waiter->link);
|
||||
tsk = waiter->waiter;
|
||||
/* Write inode state before clearing waiter. */
|
||||
smp_store_release(&waiter->waiter, NULL);
|
||||
wake_up_process(tsk);
|
||||
put_task_struct(tsk);
|
||||
} else {
|
||||
clear_bit_unlock(NETFS_ICTX_WB_LOCK, &ictx->flags);
|
||||
}
|
||||
|
||||
spin_unlock(&ictx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(netfs_wb_end);
|
||||
|
|
|
|||
|
|
@ -408,6 +408,16 @@ bool netfs_write_collection(struct netfs_io_request *wreq)
|
|||
netfs_wake_rreq_flag(wreq, NETFS_RREQ_IN_PROGRESS, netfs_rreq_trace_wake_ip);
|
||||
/* As we cleared NETFS_RREQ_IN_PROGRESS, we acquired its ref. */
|
||||
|
||||
switch (wreq->origin) {
|
||||
case NETFS_WRITEBACK:
|
||||
case NETFS_WRITEBACK_SINGLE:
|
||||
case NETFS_WRITETHROUGH:
|
||||
netfs_wb_end(ictx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (wreq->iocb) {
|
||||
size_t written = min(wreq->transferred, wreq->len);
|
||||
wreq->iocb->ki_pos += written;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
|
|||
_enter("R=%x", wreq->debug_id);
|
||||
|
||||
ictx = netfs_inode(wreq->inode);
|
||||
if (is_cacheable && netfs_is_cache_enabled(ictx))
|
||||
if (is_cacheable)
|
||||
fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx));
|
||||
if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0)
|
||||
goto nomem;
|
||||
|
|
@ -551,14 +551,8 @@ int netfs_writepages(struct address_space *mapping,
|
|||
struct folio *folio;
|
||||
int error = 0;
|
||||
|
||||
if (!mutex_trylock(&ictx->wb_lock)) {
|
||||
if (wbc->sync_mode == WB_SYNC_NONE) {
|
||||
netfs_stat(&netfs_n_wb_lock_skip);
|
||||
return 0;
|
||||
}
|
||||
netfs_stat(&netfs_n_wb_lock_wait);
|
||||
mutex_lock(&ictx->wb_lock);
|
||||
}
|
||||
if (!netfs_wb_begin(ictx, wbc->sync_mode == WB_SYNC_NONE))
|
||||
return 0;
|
||||
|
||||
/* Need the first folio to be able to set up the op. */
|
||||
folio = writeback_iter(mapping, wbc, NULL, &error);
|
||||
|
|
@ -588,13 +582,13 @@ int netfs_writepages(struct address_space *mapping,
|
|||
}
|
||||
|
||||
error = netfs_write_folio(wreq, wbc, folio);
|
||||
if (error < 0)
|
||||
break;
|
||||
if (error == -ENOMEM) {
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
folio_unlock(folio);
|
||||
}
|
||||
} while ((folio = writeback_iter(mapping, wbc, folio, &error)));
|
||||
|
||||
netfs_end_issue_write(wreq);
|
||||
|
||||
mutex_unlock(&ictx->wb_lock);
|
||||
netfs_wake_collector(wreq);
|
||||
|
||||
netfs_put_request(wreq, netfs_rreq_trace_put_return);
|
||||
|
|
@ -602,9 +596,16 @@ int netfs_writepages(struct address_space *mapping,
|
|||
return error;
|
||||
|
||||
couldnt_start:
|
||||
netfs_kill_dirty_pages(mapping, wbc, folio);
|
||||
if (error == -ENOMEM) {
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
folio_unlock(folio);
|
||||
folio = writeback_iter(mapping, wbc, folio, &error);
|
||||
WARN_ON_ONCE(folio != NULL);
|
||||
} else {
|
||||
netfs_kill_dirty_pages(mapping, wbc, folio);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ictx->wb_lock);
|
||||
netfs_wb_end(ictx);
|
||||
_leave(" = %d", error);
|
||||
return error;
|
||||
}
|
||||
|
|
@ -618,16 +619,17 @@ struct netfs_io_request *netfs_begin_writethrough(struct kiocb *iocb, size_t len
|
|||
struct netfs_io_request *wreq = NULL;
|
||||
struct netfs_inode *ictx = netfs_inode(file_inode(iocb->ki_filp));
|
||||
|
||||
mutex_lock(&ictx->wb_lock);
|
||||
netfs_wb_begin(ictx, false);
|
||||
|
||||
wreq = netfs_create_write_req(iocb->ki_filp->f_mapping, iocb->ki_filp,
|
||||
iocb->ki_pos, NETFS_WRITETHROUGH);
|
||||
if (IS_ERR(wreq)) {
|
||||
mutex_unlock(&ictx->wb_lock);
|
||||
netfs_wb_end(ictx);
|
||||
return wreq;
|
||||
}
|
||||
|
||||
wreq->io_streams[0].avail = true;
|
||||
__set_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &wreq->flags);
|
||||
trace_netfs_write(wreq, netfs_write_trace_writethrough);
|
||||
return wreq;
|
||||
}
|
||||
|
|
@ -685,7 +687,6 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c
|
|||
ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_control *wbc,
|
||||
struct folio *writethrough_cache)
|
||||
{
|
||||
struct netfs_inode *ictx = netfs_inode(wreq->inode);
|
||||
ssize_t ret;
|
||||
|
||||
_enter("R=%x", wreq->debug_id);
|
||||
|
|
@ -699,8 +700,6 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c
|
|||
|
||||
netfs_end_issue_write(wreq);
|
||||
|
||||
mutex_unlock(&ictx->wb_lock);
|
||||
|
||||
if (wreq->iocb)
|
||||
ret = -EIOCBQUEUED;
|
||||
else
|
||||
|
|
@ -847,15 +846,10 @@ int netfs_writeback_single(struct address_space *mapping,
|
|||
if (WARN_ON_ONCE(!iov_iter_is_folioq(iter)))
|
||||
return -EIO;
|
||||
|
||||
if (!mutex_trylock(&ictx->wb_lock)) {
|
||||
if (wbc->sync_mode == WB_SYNC_NONE) {
|
||||
/* The VFS will have undirtied the inode. */
|
||||
netfs_single_mark_inode_dirty(&ictx->inode);
|
||||
netfs_stat(&netfs_n_wb_lock_skip);
|
||||
return 1;
|
||||
}
|
||||
netfs_stat(&netfs_n_wb_lock_wait);
|
||||
mutex_lock(&ictx->wb_lock);
|
||||
if (!netfs_wb_begin(ictx, wbc->sync_mode == WB_SYNC_NONE)) {
|
||||
/* The VFS will have undirtied the inode. */
|
||||
netfs_single_mark_inode_dirty(&ictx->inode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wreq = netfs_create_write_req(mapping, NULL, 0, NETFS_WRITEBACK_SINGLE);
|
||||
|
|
@ -893,7 +887,6 @@ int netfs_writeback_single(struct address_space *mapping,
|
|||
smp_wmb(); /* Write lists before ALL_QUEUED. */
|
||||
set_bit(NETFS_RREQ_ALL_QUEUED, &wreq->flags);
|
||||
|
||||
mutex_unlock(&ictx->wb_lock);
|
||||
netfs_wake_collector(wreq);
|
||||
|
||||
netfs_put_request(wreq, netfs_rreq_trace_put_return);
|
||||
|
|
@ -901,7 +894,7 @@ int netfs_writeback_single(struct address_space *mapping,
|
|||
return ret;
|
||||
|
||||
couldnt_start:
|
||||
mutex_unlock(&ictx->wb_lock);
|
||||
netfs_wb_end(ictx);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,14 +61,16 @@ struct netfs_inode {
|
|||
#if IS_ENABLED(CONFIG_FSCACHE)
|
||||
struct fscache_cookie *cache;
|
||||
#endif
|
||||
struct mutex wb_lock; /* Writeback serialisation */
|
||||
struct list_head wb_queue; /* Queue of processes wanting to do writeback */
|
||||
loff_t _remote_i_size; /* Size of the remote file */
|
||||
loff_t _zero_point; /* Size after which we assume there's no data
|
||||
* on the server */
|
||||
spinlock_t lock; /* Lock covering wb_queue */
|
||||
atomic_t io_count; /* Number of outstanding reqs */
|
||||
unsigned long flags;
|
||||
#define NETFS_ICTX_ODIRECT 0 /* The file has DIO in progress */
|
||||
#define NETFS_ICTX_UNBUFFERED 1 /* I/O should not use the pagecache */
|
||||
#define NETFS_ICTX_WB_LOCK 2 /* Writeback serialisation lock */
|
||||
#define NETFS_ICTX_MODIFIED_ATTR 3 /* Indicate change in mtime/ctime */
|
||||
#define NETFS_ICTX_SINGLE_NO_UPLOAD 4 /* Monolithic payload, cache but no upload */
|
||||
};
|
||||
|
|
@ -462,6 +464,10 @@ int netfs_alloc_folioq_buffer(struct address_space *mapping,
|
|||
size_t *_cur_size, ssize_t size, gfp_t gfp);
|
||||
void netfs_free_folioq_buffer(struct folio_queue *fq);
|
||||
|
||||
/* Writeback exclusion API. */
|
||||
bool netfs_wb_begin(struct netfs_inode *ictx, bool nowait);
|
||||
void netfs_wb_end(struct netfs_inode *ictx);
|
||||
|
||||
/**
|
||||
* netfs_inode - Get the netfs inode context from the inode
|
||||
* @inode: The inode to query
|
||||
|
|
@ -743,7 +749,8 @@ static inline void netfs_inode_init(struct netfs_inode *ctx,
|
|||
#if IS_ENABLED(CONFIG_FSCACHE)
|
||||
ctx->cache = NULL;
|
||||
#endif
|
||||
mutex_init(&ctx->wb_lock);
|
||||
INIT_LIST_HEAD(&ctx->wb_queue);
|
||||
spin_lock_init(&ctx->lock);
|
||||
/* ->releasepage() drives zero_point */
|
||||
if (use_zero_point) {
|
||||
ctx->_zero_point = ctx->_remote_i_size;
|
||||
|
|
@ -753,7 +760,7 @@ static inline void netfs_inode_init(struct netfs_inode *ctx,
|
|||
|
||||
/**
|
||||
* netfs_resize_file - Note that a file got resized
|
||||
* @ctx: The netfs inode being resized
|
||||
* @ictx: The netfs inode being resized
|
||||
* @new_i_size: The new file size
|
||||
* @changed_on_server: The change was applied to the server
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1568,6 +1568,7 @@ static ssize_t iov_iter_extract_xarray_pages(struct iov_iter *i,
|
|||
struct folio *folio;
|
||||
unsigned int nr = 0, offset;
|
||||
loff_t pos = i->xarray_start + i->iov_offset;
|
||||
bool will_alloc = !*pages;
|
||||
XA_STATE(xas, i->xarray, pos >> PAGE_SHIFT);
|
||||
|
||||
offset = pos & ~PAGE_MASK;
|
||||
|
|
@ -1595,6 +1596,14 @@ static ssize_t iov_iter_extract_xarray_pages(struct iov_iter *i,
|
|||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!nr) {
|
||||
if (will_alloc) {
|
||||
kvfree(*pages);
|
||||
*pages = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
maxsize = min_t(size_t, nr * PAGE_SIZE - offset, maxsize);
|
||||
iov_iter_advance(i, maxsize);
|
||||
return maxsize;
|
||||
|
|
@ -1628,6 +1637,8 @@ static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i,
|
|||
bi.bi_bvec_done = skip;
|
||||
|
||||
maxpages = want_pages_array(pages, maxsize, skip, maxpages);
|
||||
if (!maxpages)
|
||||
return -ENOMEM;
|
||||
|
||||
while (bi.bi_size && bi.bi_idx < i->nr_segs) {
|
||||
struct bio_vec bv = bvec_iter_bvec(i->bvec, bi);
|
||||
|
|
@ -1745,6 +1756,7 @@ static ssize_t iov_iter_extract_user_pages(struct iov_iter *i,
|
|||
unsigned long addr;
|
||||
unsigned int gup_flags = 0;
|
||||
size_t offset;
|
||||
bool will_alloc = !*pages;
|
||||
int res;
|
||||
|
||||
if (i->data_source == ITER_DEST)
|
||||
|
|
@ -1761,8 +1773,14 @@ static ssize_t iov_iter_extract_user_pages(struct iov_iter *i,
|
|||
if (!maxpages)
|
||||
return -ENOMEM;
|
||||
res = pin_user_pages_fast(addr, maxpages, gup_flags, *pages);
|
||||
if (unlikely(res <= 0))
|
||||
if (unlikely(res <= 0)) {
|
||||
if (will_alloc) {
|
||||
kvfree(*pages);
|
||||
*pages = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
maxsize = min_t(size_t, maxsize, res * PAGE_SIZE - offset);
|
||||
iov_iter_advance(i, maxsize);
|
||||
return maxsize;
|
||||
|
|
|
|||
|
|
@ -1366,6 +1366,7 @@ static ssize_t extract_xarray_to_sg(struct iov_iter *iter,
|
|||
sg_max--;
|
||||
|
||||
maxsize -= len;
|
||||
start += len;
|
||||
ret += len;
|
||||
if (maxsize <= 0 || sg_max == 0)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ static void __init iov_kunit_copy_to_bvec(struct kunit *test)
|
|||
struct page **spages, **bpages;
|
||||
u8 *scratch, *buffer;
|
||||
size_t bufsize, npages, size, copied;
|
||||
int i, b, patt;
|
||||
int i, patt;
|
||||
|
||||
bufsize = 0x100000;
|
||||
npages = bufsize / PAGE_SIZE;
|
||||
|
|
@ -306,10 +306,9 @@ static void __init iov_kunit_copy_to_bvec(struct kunit *test)
|
|||
KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
|
||||
|
||||
/* Build the expected image in the scratch buffer. */
|
||||
b = 0;
|
||||
patt = 0;
|
||||
memset(scratch, 0, bufsize);
|
||||
for (pr = bvec_test_ranges; pr->from >= 0; pr++, b++) {
|
||||
for (pr = bvec_test_ranges; pr->from >= 0; pr++) {
|
||||
u8 *p = scratch + pr->page * PAGE_SIZE;
|
||||
|
||||
for (i = pr->from; i < pr->to; i++)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user