Received: by 10.223.185.116 with SMTP id b49csp1396605wrg; Wed, 14 Feb 2018 16:54:33 -0800 (PST) X-Google-Smtp-Source: AH8x224yvyi0qxHsLDq39m+tiC01ENO83YBb385PWvRVD0ULD2s0lrBmL8AGllSbMtXw3a75HUW0 X-Received: by 10.99.122.12 with SMTP id v12mr719451pgc.128.1518656073025; Wed, 14 Feb 2018 16:54:33 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1518656072; cv=none; d=google.com; s=arc-20160816; b=sXuM/xhbVOSijNtjTshpvY7KOzxRmcoQ2pGwJXE2Jpgc6pk/UZIOgpcBv4cuRoxcK9 LMg4fGrhdWBA6BkhyATtNtmPdo0gE1l5Of3zMdL0jDVyNcg/PXEVGiTRdsquX6zTtOuS vk5Jqouk9uG+czoL18aE7i2W2JpF6req61294THfve3Jo861//8cL7uDtZXoqWsQvPoX t2rGEZwnXEP7yeV3AU5SFUI7TpUkjhvR+QTxoFUJwbP7/h09VUdKepmT2EMJDoP6lY5N k5G/V8ferO4VuV32u81el3uLTw9yZ1RQdWl2ShT89ZsbN0PCv+x3rYWyIfdhzZ2jXPb4 Yfww== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from :dkim-signature:arc-authentication-results; bh=/nZd7gU8kEz7Fryac3QV4JBKXs1XCLkun4HE2BhQIpU=; b=JPS/p/oTKIivxnpu3evVoySBbQKRdM/MARp4J0RJutfnzRBTRVQvXPdoiYTICn6O6H XJxr3q53xH3NbBOqpwYvVeBkxkwOiRw2iJ8Yeub6Va6I1WESforpGVbs2jP08sW/2NPO RLKFFlvbdXsGbNioar7iOv/RjqNU1LBsGM68aoY9SVSXCjbKDGhWfCIL3BVwzXEQPJrx 9PJBRU8hoHJVCKYX5+z8ydt+cc3ZMOgxkn4iqr977twpYI4mzh3e+Uvul+b3UcPLNYF8 Ij47HcGfNtzC/091u58k2JbjLQiL3Tzxxj+oLC1h4oOy+pSRszzlwtHmQ/5LweSFwnR3 kPaw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ksZr+oqL; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 30-v6si507883pld.181.2018.02.14.16.54.18; Wed, 14 Feb 2018 16:54:32 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ksZr+oqL; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1032373AbeBOAxk (ORCPT + 99 others); Wed, 14 Feb 2018 19:53:40 -0500 Received: from mail-pg0-f68.google.com ([74.125.83.68]:40809 "EHLO mail-pg0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1032245AbeBOAxi (ORCPT ); Wed, 14 Feb 2018 19:53:38 -0500 Received: by mail-pg0-f68.google.com with SMTP id g2so2956187pgn.7; Wed, 14 Feb 2018 16:53:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=/nZd7gU8kEz7Fryac3QV4JBKXs1XCLkun4HE2BhQIpU=; b=ksZr+oqLAc9HhZOpn1M9VotrgEqU4gCm+P99QlsSsf+3C1iViI0NaIGTcCYeYB1xZ8 4bLveybKdphm9Uo+WXikVupInnkGuqpya1G8kmLB2V9UUY5hcMQTnoKfidRfOB3A60zm 8f06Eh2lP42UUclj/mUPD8pjw47TQ1NEIe7aKgM9ADVaZby5NYaNUtbhCi29wYi2zpL9 odA5dAZ0ZysV/K8qtzt6FQXy4L3O9WlYPgHwfL9QcdGnSh14vTnZF3IdR2e1HXDJ6zD+ 55LObMoZQ3OSi+zvPekP+wnDaS28Qme+8jSAD+IkNV7F9buS8WvZp+XABYCbWyCspcQo 9m8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=/nZd7gU8kEz7Fryac3QV4JBKXs1XCLkun4HE2BhQIpU=; b=e4n+HCHv4zPnTB/Of2+o0tcn+37UjCxg5l1yY5alBNCjJqdzhIFbC3pSLva5CiuFHd jbX4kqN6TCvJGMi+Cc4Vhc7y+75X5nsqyBxJCtevW/4EwXUQNtlWnrO5/DKJLuwH4N7E wIjv8IK1OgQX1F/kqNJCjjT/1G6VklnZnmtlPezBoqvUNGnMHcgra3lVqrmqs1Zs57fk plkhkjNjcMQMJgR+sLqyQ5WYxTzz9a6OYV/PjZ6DE7U4vlYOwOXL/X8Vum5LhiG5EPoe ZGhG26FoWCidLUnBCOrelqYjltNmFsnCcgt7lYh5KI0UnKmGWySpRIgpc8fAoj/ijIrY vtwQ== X-Gm-Message-State: APf1xPBqDLGFmOe5vjCp1Haj9umelpcT1F5vsPzwR5BO9vaR29kYZgS6 etH68zbXjKWbA1Em8zuBZjLVUw== X-Received: by 10.98.159.200 with SMTP id v69mr857020pfk.236.1518656017576; Wed, 14 Feb 2018 16:53:37 -0800 (PST) Received: from localhost.localdomain (c-73-93-215-6.hsd1.ca.comcast.net. [73.93.215.6]) by smtp.gmail.com with ESMTPSA id e23sm38934012pff.144.2018.02.14.16.53.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 14 Feb 2018 16:53:36 -0800 (PST) From: frowand.list@gmail.com To: Rob Herring , cpandya@codeaurora.org Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3] of: cache phandle nodes to reduce cost of of_find_node_by_phandle() Date: Wed, 14 Feb 2018 16:52:59 -0800 Message-Id: <1518655979-10910-1-git-send-email-frowand.list@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Frank Rowand Create a cache of the nodes that contain a phandle property. Use this cache to find the node for a given phandle value instead of scanning the devicetree to find the node. If the phandle value is not found in the cache, of_find_node_by_phandle() will fall back to the tree scan algorithm. The cache is initialized in of_core_init(). The cache is freed via a late_initcall_sync() if modules are not enabled. If the devicetree is created by the dtc compiler, with all phandle property values auto generated, then the size required by the cache could be 4 * (1 + number of phandles) bytes. This results in an O(1) node lookup cost for a given phandle value. Due to a concern that the phandle property values might not be consistent with what is generated by the dtc compiler, a mask has been added to the cache lookup algorithm. To maintain the O(1) node lookup cost, the size of the cache has been increased by rounding the number of entries up to the next power of two. The overhead of finding the devicetree node containing a given phandle value has been noted by several people in the recent past, in some cases with a patch to add a hashed index of devicetree nodes, based on the phandle value of the node. One concern with this approach is the extra space added to each node. This patch takes advantage of the phandle property values auto generated by the dtc compiler, which begin with one and monotonically increase by one, resulting in a range of 1..n for n phandle values. This implementation should also provide a good reduction of overhead for any range of phandle values that are mostly in a monotonic range. Performance measurements by Chintan Pandya of several implementations of patches that are similar to this one suggest an expected reduction of boot time by ~400ms for his test system. If the cache size was decreased to 64 entries, the boot time was reduced by ~340 ms. The measurements were on a 4.9.73 kernel for arch/arm64/boot/dts/qcom/sda670-mtp.dts, contains 2371 nodes and 814 phandle values. Reported-by: Chintan Pandya Signed-off-by: Frank Rowand --- A follow on patch will add an early boot allocation of the cache. Changes since v2: - add mask to calculation of phandle cache entry - which results in better overhead reduction for devicetrees with phandle properties not allocated in the monotonically increasing range of 1..n - due to mask, number of entries in cache potentially increased to next power of two - minor fixes as suggested by reviewers - no longer using live_tree_max_phandle() so do not move it from drivers/of/resolver.c to drivers/of/base.c Changes since v1: - change short description from of: cache phandle nodes to reduce cost of of_find_node_by_phandle() - rebase on v4.16-rc1 - reorder new functions in base.c to avoid forward declaration - add locking around kfree(phandle_cache) for memory ordering - add explicit check for non-null of phandle_cache in of_find_node_by_phandle(). There is already a check for !handle, which prevents accessing a null phandle_cache, but that dependency is not obvious, so this check makes it more apparent. - do not free phandle_cache if modules are enabled, so that cached phandles will be available when modules are loaded drivers/of/base.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++--- drivers/of/of_private.h | 3 ++ drivers/of/resolver.c | 3 -- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index ad28de96e13f..ab545dfa9173 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -91,10 +91,69 @@ int __weak of_node_to_nid(struct device_node *np) } #endif +static struct device_node **phandle_cache; +static u32 phandle_cache_mask; + +/* + * Assumptions behind phandle_cache implementation: + * - phandle property values are in a contiguous range of 1..n + * + * If the assumptions do not hold, then + * - the phandle lookup overhead reduction provided by the cache + * will likely be less + */ +static void of_populate_phandle_cache(void) +{ + unsigned long flags; + u32 cache_entries; + struct device_node *np; + u32 phandles = 0; + + raw_spin_lock_irqsave(&devtree_lock, flags); + + kfree(phandle_cache); + phandle_cache = NULL; + + for_each_of_allnodes(np) + if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) + phandles++; + + cache_entries = roundup_pow_of_two(phandles); + phandle_cache_mask = cache_entries - 1; + + phandle_cache = kcalloc(cache_entries, sizeof(*phandle_cache), + GFP_ATOMIC); + + for_each_of_allnodes(np) + if (np->phandle && np->phandle != OF_PHANDLE_ILLEGAL) + phandle_cache[np->phandle & phandle_cache_mask] = np; + + raw_spin_unlock_irqrestore(&devtree_lock, flags); +} + +#ifndef CONFIG_MODULES +static int __init of_free_phandle_cache(void) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&devtree_lock, flags); + + kfree(phandle_cache); + phandle_cache = NULL; + + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + return 0; +} +late_initcall_sync(of_free_phandle_cache); +#endif + void __init of_core_init(void) { struct device_node *np; + of_populate_phandle_cache(); + /* Create the kset, and register existing nodes */ mutex_lock(&of_mutex); of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj); @@ -1021,16 +1080,32 @@ int of_modalias_node(struct device_node *node, char *modalias, int len) */ struct device_node *of_find_node_by_phandle(phandle handle) { - struct device_node *np; + struct device_node *np = NULL; unsigned long flags; + phandle masked_handle; if (!handle) return NULL; raw_spin_lock_irqsave(&devtree_lock, flags); - for_each_of_allnodes(np) - if (np->phandle == handle) - break; + + masked_handle = handle & phandle_cache_mask; + + if (phandle_cache) { + if (phandle_cache[masked_handle] && + handle == phandle_cache[masked_handle]->phandle) + np = phandle_cache[masked_handle]; + } + + if (!np) { + for_each_of_allnodes(np) + if (np->phandle == handle) { + if (phandle_cache) + phandle_cache[masked_handle] = np; + break; + } + } + of_node_get(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 0c609e7d0334..fa70650136b4 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -131,6 +131,9 @@ extern void __of_update_property_sysfs(struct device_node *np, extern void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop); +/* illegal phandle value (set when unresolved) */ +#define OF_PHANDLE_ILLEGAL 0xdeadbeef + /* iterators for transactions, used for overlays */ /* forward iterator */ #define for_each_transaction_entry(_oft, _te) \ diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index 740d19bde601..b2ca8185c8c6 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -19,9 +19,6 @@ #include "of_private.h" -/* illegal phandle value (set when unresolved) */ -#define OF_PHANDLE_ILLEGAL 0xdeadbeef - static phandle live_tree_max_phandle(void) { struct device_node *node; -- Frank Rowand