From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA89A345729 for ; Thu, 18 Jun 2026 17:11:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781802706; cv=none; b=CpyoFea/XBwxJh+eH2tvTENaMsnaji2FRdxvTuqc8i0qwDU5QXsnM/zTU2uiuShAO1F1yZnv0NdEaQVPQlmJ3hs0/JbuIjNquSBcG4tE0F6jCU6ggopzjhSLjB/0p+K49jeHegea/TJhSf+b007yDFMF3BBe6h76by8K3yUUV7w= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781802706; c=relaxed/simple; bh=HLNHW16m6QmhnXKv6CJJ1xEQMPH7EFRLqNBGQo5XHdM=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=pwQhudB5EjXrvIAzCgyNvGkWb75ST9nriaXOkh1XkGXr7btyWs6O9XbSJ64ZAJScURvaOR7LOOxx/ZPdVvELYrFWoSRja2UcmGZsXDAVniEn1T5Gpk1w2YvMrgmWSSbNJUmUT3nVorLevoAG9r0ZaS1juPRz+f/UdXrGH88cRqg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (2048-bit key) header.d=suse.com header.i=@suse.com header.b=d9x8Hyex; arc=none smtp.client-ip=209.85.221.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=suse.com header.i=@suse.com header.b="d9x8Hyex" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-4629051c946so1026361f8f.1 for ; Thu, 18 Jun 2026 10:11:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=google; t=1781802702; x=1782407502; darn=vger.kernel.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=EDK5L98Pl00zZKxGb19xeAnhkIpTd5oohYUbmAno6OU=; b=d9x8Hyextb+EFixfocXZHjcAofvMju4g38cvhsJEJhS7+KSc/hc5/jLNuEBXhbTSi3 QuoPRIzH7B+xbFGLHev/P7wHiH6jPP4vR8Ghqy/p7UljQ9gwM+pmYccZCmdTqnsWTwSZ DutyQkno6KGsp+HfUMoybG5Z/lpBbG/qLwvzaw1u3TwUMfZvuqFVh1/u/j9OSzBdKcDA Qyc4qHtTBbInXsKrUWRbFrGPkBVM+iyqjgZjXtozVMN0yJt7Tv8oWONjJcoCVJlTy1Wp AOFt+eTef8NHsY2U1wOipA6cE9r/I1QkBdYmzBG9WaPZ6EoSse4dExPvRZqujlw19QiH FZtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781802702; x=1782407502; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=EDK5L98Pl00zZKxGb19xeAnhkIpTd5oohYUbmAno6OU=; b=tMqKQHtU6KbGLIsfee8/+Su7RsQCkt9ZCrnj9yGlVbrQ6fn/WOxtOFCNQ1BT8VhnHK ifX2pontTClE5NTOq4eZYn0m8QICVYqKDzz3QNhbqiKeUeiSYvfuvf+dkJd6yI0Q6Bwv n0MD6kyEBMgMOuMNhDk3fpotiLEebKKZDbGcmcxTVNnKNKiDPdW1zQaOBv8CPm+f7OuL o4lU7c8rcxxW8DwW9nCqsJV8FhWDqtSpAqbi4SRhzD7Klos3XLf8gSPjt2KTlXWD+YIZ iD4qm+WljJaOBtq+FgyvNCcbpOf7yOdesAKyqv2wGFP4IPNLcTjwRkwXnlvmZPRUo91d Yf+A== X-Forwarded-Encrypted: i=1; AFNElJ+ApNPx5LyeoqzLG+4p9HqKlzmtfVCxji/u3XfHo6vet+7GbWzbfvNapS+Gwoo4N/b6EBTPUxSR96rh7UI=@vger.kernel.org X-Gm-Message-State: AOJu0YwbIsXah5VSWBGJ0Vio2K/MbR4S/3Y65eptDU8BiNOlDbG2q7pS 7Vkwye0WSqlrmQYAGBHJOXhkD5KXryjfVfqNq+r/tEIbYLjXJ0j7LwrxwTNWeMaPfgI= X-Gm-Gg: AfdE7cnpMjnyZTipZrj4TpnSYad02HKARBp/xNkkqjT4Q5Pg/oY7uEzPiT4RPLj5wMq fjFH3pk8ekn9GFcktCrcmcCM6GBeD8YBm6bpt02fgkE/oOUohRZQGPFwDGrp3NeIUnRzSHaFs3G D9cBwyVoyXcTtP/Z4zPZ96Ypsly+sdQapWJmvbuw/66YDhRR84N9ybZukWajntIuMSvoixdjjg/ a8C0HIHXDl3r23x2RJYK5/MUQXYnGVUqxMRuVcN+/kiMIyjvPBom4xqQD8+WfDCrZFmZJEEiFU3 VS7Ejkf2mRrCVBfumg8DEmFWToKEM63fXNLPO1GsoL0dr+YYnp1dLXRFAb83loeNXbp8e37pkTb 2n5mmR5XG14r9Lu3Nt7ZDeG7dzm5m5CUu65qtfzE4IVzxlZH2o3VpklfFpQHFcc1rk+9qijsQNd PzjIgq6ahou0Pbf1yaBdQ/fEIoTw== X-Received: by 2002:a05:6000:d05:b0:45a:5392:3a19 with SMTP id ffacd0b85a97d-4639834c628mr5752222f8f.16.1781802702126; Thu, 18 Jun 2026 10:11:42 -0700 (PDT) Received: from localhost (109-81-26-193.rct.o2.cz. [109.81.26.193]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4650bc429adsm265230f8f.32.2026.06.18.10.11.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 18 Jun 2026 10:11:41 -0700 (PDT) Date: Thu, 18 Jun 2026 19:11:40 +0200 From: Michal Hocko To: Kaitao Cheng Cc: Andrew Morton , Uladzislau Rezki , Dennis Zhou , Tejun Heo , Christoph Lameter , Vlastimil Babka , Pedro Falcato , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Kaitao Cheng Subject: Re: [PATCH v4 2/4] mm/percpu: honor GFP constraints when populating chunks Message-ID: References: <20260618130414.96383-1-kaitao.cheng@linux.dev> <20260618130414.96383-3-kaitao.cheng@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20260618130414.96383-3-kaitao.cheng@linux.dev> On Thu 18-06-26 21:04:12, Kaitao Cheng wrote: > From: Kaitao Cheng > > pcpu_alloc_noprof() derives pcpu_gfp from the caller supplied GFP mask and > passes it down to pcpu_populate_chunk(). pcpu_alloc_pages() already uses > that mask for backing page allocation. > > However, the populate slow path still has internal allocations and page > table allocations which can lose the caller's allocation context. The > temporary pages array is allocated by pcpu_get_pages() with GFP_KERNEL, > and pcpu_map_pages() maps the backing pages through > vmap_pages_range_noflush() using GFP_KERNEL. The latter can allocate > vmalloc page tables implicitly, so a caller which deliberately uses > GFP_NOFS or GFP_NOIO can still enter FS or IO reclaim while populating > a percpu chunk. > > This has the same concern as chunk creation: callers such as blk-cgroup > may use GFP_NOIO because they hold locks which can be involved in queue > freeze or IO reclaim dependencies. If an allocation reaches the percpu > slow path and needs to populate previously unbacked pages, the internal > GFP_KERNEL allocations can defeat that context. > > One possible case is blk-cgroup after commit 5d726c4dbeed > ("blk-cgroup: fix possible deadlock while configuring policy"). > blkg_conf_prep() now serializes against blkcg_deactivate_policy() with > q->blkcg_mutex, and blkg_alloc() was changed to GFP_NOIO for that reason: > > CPU0: blkg_conf_prep() > mutex_lock(q->blkcg_mutex) > blkg_alloc(..., GFP_NOIO) > alloc_percpu_gfp(..., GFP_NOIO) > pcpu_alloc_noprof(..., GFP_NOIO) > pcpu_populate_chunk(GFP_NOIO) > pcpu_get_pages() > pcpu_map_pages() > -> if the selected percpu chunk has unpopulated pages, > chunk population may do internal GFP_KERNEL allocations > -> direct reclaim / writeback can issue IO to this queue > -> IO waits because the queue is frozen > > CPU1: blkcg_deactivate_policy() > blk_mq_freeze_queue(q) > mutex_lock(q->blkcg_mutex) > -> waits for CPU0 > ... unfreeze only happens after q->blkcg_mutex is acquired/released > > So the concern is that the caller deliberately uses GFP_NOIO because it > may hold a lock which can be acquired after queue freeze, but the percpu > slow path can temporarily lose that allocation context. > > Pass pcpu_gfp through pcpu_get_pages(), pcpu_map_pages() and > __pcpu_map_pages(). Apply the corresponding memalloc scope around > vmap_pages_range_noflush(), because vmalloc page table allocation does not > pass the GFP mask down explicitly. Keep the first chunk setup path using > GFP_KERNEL, matching the previous early-init behavior. > > Fixes: 9a5b183941b5 ("mm, percpu: do not consider sleepable allocations atomic") > Signed-off-by: Kaitao Cheng > Acked-by: Dennis Zhou OK, seems sensible. Acked-by: Michal Hocko Thanks! > --- > mm/percpu-vm.c | 38 ++++++++++++++++++++++++++------------ > mm/percpu.c | 2 +- > 2 files changed, 27 insertions(+), 13 deletions(-) > > diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c > index 69b00741dc68..ccd03cc152d4 100644 > --- a/mm/percpu-vm.c > +++ b/mm/percpu-vm.c > @@ -21,6 +21,7 @@ static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk, > > /** > * pcpu_get_pages - get temp pages array > + * @gfp: allocation flags passed to the underlying allocator > * > * Returns pointer to array of pointers to struct page which can be indexed > * with pcpu_page_idx(). Note that there is only one array and accesses > @@ -29,7 +30,7 @@ static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk, > * RETURNS: > * Pointer to temp pages array on success. > */ > -static struct page **pcpu_get_pages(void) > +static struct page **pcpu_get_pages(gfp_t gfp) > { > static struct page **pages; > size_t pages_size = pcpu_nr_units * pcpu_unit_pages * sizeof(pages[0]); > @@ -37,7 +38,7 @@ static struct page **pcpu_get_pages(void) > lockdep_assert_held(&pcpu_alloc_mutex); > > if (!pages) > - pages = pcpu_mem_zalloc(pages_size, GFP_KERNEL); > + pages = pcpu_mem_zalloc(pages_size, gfp); > return pages; > } > > @@ -191,10 +192,22 @@ static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk, > } > > static int __pcpu_map_pages(unsigned long addr, struct page **pages, > - int nr_pages) > + int nr_pages, gfp_t gfp) > { > - return vmap_pages_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT), > - PAGE_KERNEL, pages, PAGE_SHIFT, GFP_KERNEL); > + unsigned int flags; > + int ret; > + > + /* > + * The vmalloc page table allocation path does not pass @gfp down > + * explicitly. Apply the corresponding memalloc scope so implicit > + * page table allocations preserve NOFS/NOIO constraints. > + */ > + flags = memalloc_apply_gfp_scope(gfp); > + ret = vmap_pages_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT), > + PAGE_KERNEL, pages, PAGE_SHIFT, gfp); > + memalloc_restore_scope(flags); > + > + return ret; > } > > /** > @@ -203,6 +216,7 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages, > * @pages: pages array containing pages to be mapped > * @page_start: page index of the first page to map > * @page_end: page index of the last page to map + 1 > + * @gfp: allocation flags passed to the underlying allocator > * > * For each cpu, map pages [@page_start,@page_end) into @chunk. The > * caller is responsible for calling pcpu_post_map_flush() after all > @@ -211,8 +225,8 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages, > * This function is responsible for setting up whatever is necessary for > * reverse lookup (addr -> chunk). > */ > -static int pcpu_map_pages(struct pcpu_chunk *chunk, > - struct page **pages, int page_start, int page_end) > +static int pcpu_map_pages(struct pcpu_chunk *chunk, struct page **pages, > + int page_start, int page_end, gfp_t gfp) > { > unsigned int cpu, tcpu; > int i, err; > @@ -220,7 +234,7 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk, > for_each_possible_cpu(cpu) { > err = __pcpu_map_pages(pcpu_chunk_addr(chunk, cpu, page_start), > &pages[pcpu_page_idx(cpu, page_start)], > - page_end - page_start); > + page_end - page_start, gfp); > if (err < 0) > goto err; > > @@ -271,21 +285,21 @@ static void pcpu_post_map_flush(struct pcpu_chunk *chunk, > * @chunk. > * > * CONTEXT: > - * pcpu_alloc_mutex, does GFP_KERNEL allocation. > + * pcpu_alloc_mutex, does @gfp allocation. > */ > static int pcpu_populate_chunk(struct pcpu_chunk *chunk, > int page_start, int page_end, gfp_t gfp) > { > struct page **pages; > > - pages = pcpu_get_pages(); > + pages = pcpu_get_pages(gfp); > if (!pages) > return -ENOMEM; > > if (pcpu_alloc_pages(chunk, pages, page_start, page_end, gfp)) > return -ENOMEM; > > - if (pcpu_map_pages(chunk, pages, page_start, page_end)) { > + if (pcpu_map_pages(chunk, pages, page_start, page_end, gfp)) { > pcpu_free_pages(chunk, pages, page_start, page_end); > return -ENOMEM; > } > @@ -319,7 +333,7 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk, > * successful population attempt so the temp pages array must > * be available now. > */ > - pages = pcpu_get_pages(); > + pages = pcpu_get_pages(GFP_KERNEL); > BUG_ON(!pages); > > /* unmap and free */ > diff --git a/mm/percpu.c b/mm/percpu.c > index b0676b8054ed..4d89965cba16 100644 > --- a/mm/percpu.c > +++ b/mm/percpu.c > @@ -3256,7 +3256,7 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t > > /* pte already populated, the following shouldn't fail */ > rc = __pcpu_map_pages(unit_addr, &pages[unit * unit_pages], > - unit_pages); > + unit_pages, GFP_KERNEL); > if (rc < 0) > panic("failed to map percpu area, err=%d\n", rc); > > -- > 2.50.1 (Apple Git-155) -- Michal Hocko SUSE Labs