From nobody Fri Dec 19 13:57:08 2025 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 933BA2F746E; Fri, 10 Oct 2025 14:43:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760107387; cv=none; b=KwqI8192A9ZQRj8tbbDc4TMkVYr22KcmXpHQELHHY3Gtnm/o3kj4jG3WGiwccArCfv9CoDTeP61mYU8+fYWld9WefJtjsQrUW6alolk+QLTLZFLX+XTzMPueIM7VCaioNAW+2LIbpGNnTkjcGioNG1QS6Xl1gGR6mQkH6hvYpF4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760107387; c=relaxed/simple; bh=Ps0eFgpKDQwj5gTo/VReISXpz7hf3pTeaFFQ3wYZhls=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=GWIL1Qa8ZlnoX2m+gGzRRtDnYWvgwYW16lDawSEX+oB5+AIQA1MxAsg0hgEdwFJXhH8oIDbNR1FMSlzYRDACAJhlL9guNMKjpnpayVDSqhdKlT9oVUkEwg95nVybGCMRulqvpcgobrMEHHEkDhQ8QExoab3/B0ZLXJN4gA9z+FY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=SCa2/y6C; arc=none smtp.client-ip=198.175.65.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="SCa2/y6C" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1760107386; x=1791643386; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Ps0eFgpKDQwj5gTo/VReISXpz7hf3pTeaFFQ3wYZhls=; b=SCa2/y6CyDSH0x7ZTcTVVhusFwxLfQR8n/gSRCeYkKntiFXvpBK5FeFb 64GryA86hJGzRVLbwoRi0YG4tzQAVG+qPgGvVklNENYyoCjPy2+H6vrmT jE3q1ctBMrimTzbvN9UousKaGjgHeNeBfLqF1gkqB9tIuF5mDmiXbJNbt yX6Nxrg41Lq0TBYDDgHfwjswMJwozBv40xZmKt9y//n1aajt7o6SaZzOg G21I5goaOqAm/cC2nu/FLDLGdfkeyBesJytvMlK4CpLXWcaPVezkd1AFH 2e+bE7q70va0Oux+khwt+e7rPUiFV848ETAAOJn3obCIWQaqEOsygEDgl w==; X-CSE-ConnectionGUID: p/SK3P0JTGm8Bha/ip3Eag== X-CSE-MsgGUID: j0KGA7BqRX65GDZbogyj9A== X-IronPort-AV: E=McAfee;i="6800,10657,11578"; a="62370423" X-IronPort-AV: E=Sophos;i="6.19,219,1754982000"; d="scan'208";a="62370423" Received: from orviesa009.jf.intel.com ([10.64.159.149]) by orvoesa110.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 Oct 2025 07:43:05 -0700 X-CSE-ConnectionGUID: c2y819vBRTm5pmzRWCIXQg== X-CSE-MsgGUID: 7aZHqcs2SiyzZ2O2ZSco6g== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.19,219,1754982000"; d="scan'208";a="180573932" Received: from ijarvine-mobl1.ger.corp.intel.com (HELO localhost) ([10.245.245.127]) by orviesa009-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 Oct 2025 07:42:59 -0700 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= To: linux-pci@vger.kernel.org, Bjorn Helgaas , Geert Uytterhoeven , Kai-Heng Feng , Rob Herring , linux-kernel@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Krzysztof=20Wilczy=C5=84ski?= , Linux-Renesas , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [PATCH 2/3] PCI: Do not coalesce host bridge resource structs in place Date: Fri, 10 Oct 2025 17:42:30 +0300 Message-Id: <20251010144231.15773-3-ilpo.jarvinen@linux.intel.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20251010144231.15773-1-ilpo.jarvinen@linux.intel.com> References: <20251010144231.15773-1-ilpo.jarvinen@linux.intel.com> 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="utf-8" Content-Transfer-Encoding: quoted-printable The resource coalescing for host bridge windows in pci_register_host_bridge() can alter the underlying struct resource which is inherently dangerous as this code has no knowledge of who else holds a pointer the same resource. Merge the struct resource inside a newly added resource_list_merge_entries() which uses the internal __res member of the struct resource_entry to store the merged resource, thus preserving the original resource structs. Link: https://lore.kernel.org/linux-pci/CAMuHMdUjhq2ZLFyMYv9KYRW8brsvXMH5xb= 5NW8shsHJmx7w2QQ@mail.gmail.com/ Signed-off-by: Ilpo J=C3=A4rvinen --- drivers/pci/probe.c | 8 +-- include/linux/resource_ext.h | 3 + kernel/resource.c | 135 ++++++++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 6 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 04523dea7d96..053bffc17146 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1061,10 +1061,10 @@ static int pci_register_host_bridge(struct pci_host= _bridge *bridge) if (prev_res->flags !=3D res->flags || prev_offset !=3D offset) continue; =20 - if (prev_res->end + 1 =3D=3D res->start) { - res->start =3D prev_res->start; - prev_res->flags =3D prev_res->start =3D prev_res->end =3D 0; - } + if (prev_res->end + 1 !=3D res->start) + continue; + + resource_list_merge_entries(prev, window); } =20 /* Add initial resources to the bus */ diff --git a/include/linux/resource_ext.h b/include/linux/resource_ext.h index ff0339df56af..4d6f2a926e6d 100644 --- a/include/linux/resource_ext.h +++ b/include/linux/resource_ext.h @@ -60,6 +60,9 @@ resource_list_destroy_entry(struct resource_entry *entry) resource_list_free_entry(entry); } =20 +struct resource_entry *resource_list_merge_entries(struct resource_entry *= entry, + struct resource_entry *next); + #define resource_list_for_each_entry(entry, list) \ list_for_each_entry((entry), (list), node) =20 diff --git a/kernel/resource.c b/kernel/resource.c index f9bb5481501a..c6e1872abb78 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -10,6 +10,7 @@ =20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt =20 +#include #include #include #include @@ -206,6 +207,13 @@ static struct resource * __request_resource(struct res= ource *root, struct resour } } =20 +static void resource_clear_tree_links(struct resource *res) +{ + res->parent =3D NULL; + res->child =3D NULL; + res->sibling =3D NULL; +} + static int __release_resource(struct resource *old, bool release_child) { struct resource *tmp, **p, *chd; @@ -265,6 +273,101 @@ void release_child_resources(struct resource *r) write_unlock(&resource_lock); } =20 +/** + * resource_mergeable - Test if resources are contiguous and can be merged + * @r1: first resource + * @r2: second resource + * + * Tests @r1 is followed by @r2 contiguously and share the metadata. + * + * Return: %true if resources are mergeable non-destructively. + */ +static bool resource_mergeable(struct resource *r1, struct resource *r2) +{ + if ((r1->flags !=3D r2->flags) || + (r1->desc !=3D r2->desc) || + (r1->parent !=3D r2->parent) || + (r1->end + 1 !=3D r2->start)) + return false; + + if (r1->name =3D=3D r2->name) + return true; + + if (r1->name && r2->name && !strcmp(r1->name, r2->name)) + return true; + + return false; +} + +static int resource_coalesce(struct resource *res, struct resource *next_r= es, + struct resource *new_res) +{ + struct resource *parent, *tmp; + struct resource **p, **c; + + if (!resource_mergeable(res, next_res)) + return -EINVAL; + + guard(write_lock)(&resource_lock); + parent =3D res->parent; + + if (parent !=3D next_res->parent) + return -EINVAL; + + if (parent && res->sibling !=3D next_res) + return -EINVAL; + + new_res->start =3D res->start; + new_res->end =3D next_res->end; + new_res->name =3D res->name; + new_res->flags =3D res->flags; + new_res->desc =3D res->desc; + + if (!parent) + return 0; + + /* prepare for step 2), find res & next_res from child/sibling chain. */ + p =3D &parent->child; + while (1) { + tmp =3D *p; + if (tmp =3D=3D res) + break; + + /* No res in child/sibling, the resource tree is corrupted! */ + if (WARN_ON_ONCE(!tmp)) + return -EINVAL; + + p =3D &tmp->sibling; + } + + /* 1) set the parent */ + new_res->parent =3D parent; + + /* 2) inject into parent's child/sibling chain */ + *p =3D new_res; + new_res->sibling =3D next_res->sibling; + + /* 3) move children over and switch them to the new parent. */ + c =3D &new_res->child; + *c =3D res->child; + while (*c) { + tmp =3D *c; + tmp->parent =3D new_res; + c =3D &tmp->sibling; + } + *c =3D next_res->child; + while (*c) { + tmp =3D *c; + tmp->parent =3D new_res; + c =3D &tmp->sibling; + } + + resource_clear_tree_links(res); + resource_clear_tree_links(next_res); + + return 0; +} + /** * request_resource_conflict - request and reserve an I/O or memory resour= ce * @root: root resource descriptor @@ -1503,8 +1606,7 @@ static bool system_ram_resources_mergeable(struct res= ource *r1, struct resource *r2) { /* We assume either r1 or r2 is IORESOURCE_SYSRAM_MERGEABLE. */ - return r1->flags =3D=3D r2->flags && r1->end + 1 =3D=3D r2->start && - r1->name =3D=3D r2->name && r1->desc =3D=3D r2->desc && + return resource_mergeable(r1, r2) && !r1->child && !r2->child; } =20 @@ -1860,6 +1962,35 @@ void resource_list_free(struct list_head *head) } EXPORT_SYMBOL(resource_list_free); =20 +struct resource_entry *resource_list_merge_entries(struct resource_entry *= entry, + struct resource_entry *next) +{ + struct resource *res =3D entry->res, *next_res =3D next->res, *new_res; + int ret; + + if ((entry->offset !=3D next->offset) || + !resource_mergeable(res, next_res)) + return ERR_PTR(-EINVAL); + + /* Use the internal __res to not mutate the input resources. */ + struct resource_entry __free(kfree) *new =3D resource_list_create_entry(N= ULL, 0); + if (!new) + return ERR_PTR(-ENOMEM); + + new->offset =3D next->offset; + new_res =3D new->res; + + ret =3D resource_coalesce(res, next_res, new_res); + if (ret) + return ERR_PTR(ret); + + resource_list_add_tail(new, &entry->node); + resource_list_destroy_entry(entry); + resource_list_destroy_entry(next); + + return no_free_ptr(new); +} + #ifdef CONFIG_GET_FREE_REGION #define GFR_DESCENDING (1UL << 0) #define GFR_REQUEST_REGION (1UL << 1) --=20 2.39.5