Re: [syzbot] [netfs?] WARNING in netfs_writepages (3)
David Howells <[email protected]> Mon, 27 Jul 2026 13:20:21 +0100
| Newsgroups | dev.linux.lists.netfs,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Organization | Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 |
| Message-ID | <[email protected]> |
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git= master commit 6836db8d3babf7ae82597d516f89848e5652b957 Author: David Howells <[email protected]> Date: Fri Jul 24 10:24:30 2026 +0100 netfs: Fix folio_queue ENOMEM in writeback by adding a mempool =20 Fix the handling of folio_queue allocation failure in writeback by addi= ng a mempool and passing in gfp_t flags to the rolling buffer functions that allocate memory, using the mempool if gfp !=3D GFP_KERNEL. =20 This is then extended upwards and the gfp to be used for a request is s= tored in the netfs_io_request struct and is then used for both requests and subrequests, eliminating the sleeping loops there. =20 The failure caused: =20 folio !=3D NULL WARNING: fs/netfs/write_issue.c:603 at netfs_writepages+0x883/0xa10= fs/netfs/write_issue.c:603, CPU#3: syz.0.17/5919 =20 Fixes: cd0277ed0c18 ("netfs: Use new folio_queue data type and iterator= instead of xarray iter") Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=3D0da43efa72f88bd3a8af Signed-off-by: David Howells <[email protected]> Tested-by: [email protected] cc: Paulo Alcantara <[email protected]> cc: Yun Zhou <[email protected]> cc: Matthew Wilcox <[email protected]> cc: Christoph Hellwig <[email protected]> cc: [email protected] cc: [email protected] diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 3d86414ee40f..7fdfa4f27e34 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -361,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl) =09netfs_rreq_expand(rreq, ractl); =20 =09rreq->submitted =3D rreq->start; -=09if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) +=09if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq-= >gfp) < 0) =09=09goto cleanup_free; =09netfs_read_to_pagecache(rreq, ractl); =20 @@ -380,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_= io_request *rreq, struct fo { =09ssize_t added; =20 -=09if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) +=09if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq-= >gfp) < 0) =09=09return -ENOMEM; =20 -=09added =3D rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags); +=09added =3D rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rr= eq->gfp); =09if (added < 0) =09=09return added; =09rreq->submitted =3D rreq->start + added; diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index d889caa401dc..420ee7b26580 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests; extern spinlock_t netfs_proc_lock; extern mempool_t netfs_request_pool; extern mempool_t netfs_subrequest_pool; +extern mempool_t netfs_folioq_pool; =20 #ifdef CONFIG_PROC_FS static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq) diff --git a/fs/netfs/main.c b/fs/netfs/main.c index 73da6c9f5777..927badf3989d 100644 --- a/fs/netfs/main.c +++ b/fs/netfs/main.c @@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab; static struct kmem_cache *netfs_subrequest_slab; mempool_t netfs_request_pool; mempool_t netfs_subrequest_pool; +mempool_t netfs_folioq_pool; =20 #ifdef CONFIG_PROC_FS LIST_HEAD(netfs_io_requests); @@ -108,6 +109,9 @@ static int __init netfs_init(void) { =09int ret =3D -ENOMEM; =20 +=09if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct fo= lio_queue)) < 0) +=09=09goto error_folioq_pool; + =09netfs_request_slab =3D kmem_cache_create("netfs_request", =09=09=09=09=09 sizeof(struct netfs_io_request), 0, =09=09=09=09=09 SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, @@ -160,6 +164,8 @@ static int __init netfs_init(void) error_reqpool: =09kmem_cache_destroy(netfs_request_slab); error_req: +=09mempool_exit(&netfs_folioq_pool); +error_folioq_pool: =09return ret; } fs_initcall(netfs_init); @@ -172,5 +178,6 @@ static void __exit netfs_exit(void) =09kmem_cache_destroy(netfs_subrequest_slab); =09mempool_exit(&netfs_request_pool); =09kmem_cache_destroy(netfs_request_slab); +=09mempool_exit(&netfs_folioq_pool); } module_exit(netfs_exit); diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index b8c4918d3dcd..29d8eab06dec 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c @@ -7,7 +7,6 @@ =20 #include <linux/slab.h> #include <linux/mempool.h> -#include <linux/delay.h> #include "internal.h" =20 static void netfs_free_request(struct work_struct *work); @@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct add= ress_space *mapping, =09struct netfs_io_request *rreq; =09mempool_t *mempool =3D ctx->ops->request_pool ?: &netfs_request_pool; =09struct kmem_cache *cache =3D mempool->pool_data; +=09gfp_t gfp =3D GFP_KERNEL; =09int ret; =20 -=09for (;;) { -=09=09rreq =3D mempool_alloc(mempool, GFP_KERNEL); -=09=09if (rreq) -=09=09=09break; -=09=09msleep(10); +=09/* Writeback is part of memory reclaim and must not fail due to ENOMEM.= */ +=09if (origin =3D=3D NETFS_WRITEBACK || origin =3D=3D NETFS_WRITEBACK_SING= LE) { +=09=09gfp =3D GFP_NOFS; /* Allows use of mempools. */ + +=09=09rreq =3D mempool_alloc(mempool, gfp); +=09} else { +=09=09rreq =3D mempool->alloc(gfp, mempool->pool_data); +=09=09if (!rreq) +=09=09=09return ERR_PTR(-ENOMEM); =09} =20 =09memset(rreq, 0, kmem_cache_size(cache)); =09INIT_WORK(&rreq->cleanup_work, netfs_free_request); +=09rreq->gfp=09=3D gfp; =09rreq->start=09=3D start; =09rreq->len=09=3D len; =09rreq->origin=09=3D origin; @@ -200,12 +205,14 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(st= ruct netfs_io_request *rreq =09mempool_t *mempool =3D rreq->netfs_ops->subrequest_pool ?: &netfs_subre= quest_pool; =09struct kmem_cache *cache =3D mempool->pool_data; =20 -=09for (;;) { -=09=09subreq =3D mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_= subrequest_pool, -=09=09=09=09 GFP_KERNEL); -=09=09if (subreq) -=09=09=09break; -=09=09msleep(10); +=09if (rreq->gfp =3D=3D GFP_KERNEL) { +=09=09subreq =3D mempool->alloc(rreq->gfp, mempool->pool_data); +=09=09if (!subreq) +=09=09=09return ERR_PTR(-ENOMEM); +=09} else { +=09=09subreq =3D mempool_alloc(mempool, rreq->gfp); +=09=09if (!subreq) +=09=09=09return NULL; =09} =20 =09memset(subreq, 0, kmem_cache_size(cache)); diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index 7eacc58abadb..c31190993b76 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -53,7 +53,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_requ= est *creq, struct folio =09trace_netfs_folio(folio, netfs_folio_trace_store_copy); =20 =09/* Attach the folio to the rolling buffer. */ -=09if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { +=09if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) { =09=09folio_end_private_2(folio); =09=09clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); =09=09return; diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c index a17fbf9853a4..c7f107764bda 100644 --- a/fs/netfs/rolling_buffer.c +++ b/fs/netfs/rolling_buffer.c @@ -27,7 +27,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq= _id, gfp_t gfp, { =09struct folio_queue *fq; =20 -=09fq =3D kmalloc_obj(*fq, gfp); +=09if (gfp =3D=3D GFP_KERNEL) +=09=09fq =3D netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data); +=09else +=09=09fq =3D mempool_alloc(&netfs_folioq_pool, gfp); =09if (fq) { =09=09netfs_stat(&netfs_n_folioq); =09=09folioq_init(fq, rreq_id); @@ -50,7 +53,7 @@ void netfs_folioq_free(struct folio_queue *folioq, { =09trace_netfs_folioq(folioq, trace); =09netfs_stat_d(&netfs_n_folioq); -=09kfree(folioq); +=09mempool_free(folioq, &netfs_folioq_pool); } EXPORT_SYMBOL(netfs_folioq_free); =20 @@ -60,11 +63,11 @@ EXPORT_SYMBOL(netfs_folioq_free); * consumer. */ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, -=09=09=09unsigned int direction) +=09=09=09unsigned int direction, gfp_t gfp) { =09struct folio_queue *fq; =20 -=09fq =3D netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf= _init); +=09fq =3D netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init= ); =09if (!fq) =09=09return -ENOMEM; =20 @@ -77,14 +80,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, un= signed int rreq_id, /* * Add another folio_queue to a rolling buffer if there's no space left. */ -int rolling_buffer_make_space(struct rolling_buffer *roll) +int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp) { =09struct folio_queue *fq, *head =3D roll->head; =20 =09if (!folioq_full(head)) =09=09return 0; =20 -=09fq =3D netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_m= ake_space); +=09fq =3D netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_s= pace); =09if (!fq) =09=09return -ENOMEM; =09fq->prev =3D head; @@ -122,7 +125,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buff= er *roll, =09int nr, ix, to; =09ssize_t size =3D 0; =20 -=09if (rolling_buffer_make_space(roll) < 0) +=09if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0) =09=09return -ENOMEM; =20 =09fq =3D roll->head; @@ -153,12 +156,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_bu= ffer *roll, * Append a folio to the rolling buffer. */ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *f= olio, -=09=09=09 unsigned int flags) +=09=09=09 unsigned int flags, gfp_t gfp) { =09ssize_t size =3D folio_size(folio); =09int slot; =20 -=09if (rolling_buffer_make_space(roll) < 0) +=09if (rolling_buffer_make_space(roll, gfp) < 0) =09=09return -ENOMEM; =20 =09slot =3D folioq_append(roll->head, folio); diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 14efe4cb9393..2d9cfcd43658 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct = address_space *mapping, =09ictx =3D netfs_inode(wreq->inode); =09if (is_cacheable) =09=09fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie= (ictx)); -=09if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0= ) +=09if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wre= q->gfp) < 0) =09=09goto nomem; =20 =09wreq->cleaned_to =3D wreq->start; @@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq, =09 */ =09if (iov_iter_is_folioq(wreq_iter) && =09 wreq_iter->folioq_slot >=3D folioq_nr_slots(wreq_iter->folioq)) -=09=09rolling_buffer_make_space(&wreq->buffer); +=09=09rolling_buffer_make_space(&wreq->buffer, wreq->gfp); =20 =09subreq =3D netfs_alloc_subrequest(wreq); =09subreq->source=09=09=3D stream->source; @@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *w= req, =20 =09_enter(""); =20 -=09if (rolling_buffer_make_space(&wreq->buffer) < 0) +=09if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0) =09=09return -ENOMEM; =20 =09/* netfs_perform_write() may shift i_size around the page or from out @@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *w= req, =09} =20 =09/* Attach the folio to the rolling buffer. */ -=09rolling_buffer_append(&wreq->buffer, folio, 0); +=09rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp); =20 =09/* Move the submission point forward to allow for write-streaming data =09 * not starting at the front of the page. We don't do write-streaming @@ -749,7 +749,7 @@ static int netfs_write_folio_single(struct netfs_io_req= uest *wreq, =20 =09/* Attach the folio to the rolling buffer. */ =09folio_get(folio); -=09ret =3D rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_M= ARK); +=09ret =3D rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_M= ARK, wreq->gfp); =09if (ret < 0) { =09=09folio_put(folio); =09=09return ret; diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 1bc120d61c5b..d0b62d53eea9 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -255,6 +255,7 @@ struct netfs_io_request { =09unsigned long long=09cleaned_to;=09/* Position we've cleaned folios to = */ =09unsigned long long=09abandon_to;=09/* Position to abandon folios to */ =09const struct folio=09*no_unlock_folio; /* Don't unlock this folio after= read */ +=09gfp_t=09=09=09gfp;=09=09/* GFP flags to use */ =09unsigned int=09=09direct_bv_count; /* Number of elements in direct_bv[]= */ =09unsigned int=09=09debug_id; =09unsigned int=09=09rsize;=09=09/* Maximum read size (0 for none) */ diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.= h index ac15b1ffdd83..9e5dad29669c 100644 --- a/include/linux/rolling_buffer.h +++ b/include/linux/rolling_buffer.h @@ -43,13 +43,13 @@ struct rolling_buffer_snapshot { #define ROLLBUF_MARK_2=09BIT(1) =20 int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, -=09=09=09unsigned int direction); -int rolling_buffer_make_space(struct rolling_buffer *roll); +=09=09=09unsigned int direction, gfp_t gfp); +int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp); ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, =09=09=09=09 struct readahead_control *ractl, =09=09=09=09 struct folio_batch *put_batch); ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *f= olio, -=09=09=09 unsigned int flags); +=09=09=09 unsigned int flags, gfp_t gfp); struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *rol= l); void rolling_buffer_clear(struct rolling_buffer *roll); =20