summaryrefslogtreecommitdiffstats
path: root/drivers/base/node.c
diff options
context:
space:
mode:
authorKeith Busch <keith.busch@intel.com>2019-03-11 16:56:02 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-04-04 12:41:21 -0400
commitacc02a109b0497e917c83f986a89c51e47d0022c (patch)
treed91c422777692d123a0f5d42b96b6e5c76fba566 /drivers/base/node.c
parente1cf33aafb8462c7d0a0e6349925870316f040ee (diff)
node: Add memory-side caching attributes
System memory may have caches to help improve access speed to frequently requested address ranges. While the system provided cache is transparent to the software accessing these memory ranges, applications can optimize their own access based on cache attributes. Provide a new API for the kernel to register these memory-side caches under the memory node that provides it. The new sysfs representation is modeled from the existing cpu cacheinfo attributes, as seen from /sys/devices/system/cpu/<cpu>/cache/. Unlike CPU cacheinfo though, the node cache level is reported from the view of the memory. A higher level number is nearer to the CPU, while lower levels are closer to the last level memory. The exported attributes are the cache size, the line size, associativity indexing, and write back policy, and add the attributes for the system memory caches to sysfs stable documentation. Signed-off-by: Keith Busch <keith.busch@intel.com> Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Brice Goglin <Brice.Goglin@inria.fr> Tested-by: Brice Goglin <Brice.Goglin@inria.fr> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/node.c')
-rw-r--r--drivers/base/node.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 2de546a040a5..8598fcbd2a17 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -205,6 +205,155 @@ void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,
205 } 205 }
206 } 206 }
207} 207}
208
209/**
210 * struct node_cache_info - Internal tracking for memory node caches
211 * @dev: Device represeting the cache level
212 * @node: List element for tracking in the node
213 * @cache_attrs:Attributes for this cache level
214 */
215struct node_cache_info {
216 struct device dev;
217 struct list_head node;
218 struct node_cache_attrs cache_attrs;
219};
220#define to_cache_info(device) container_of(device, struct node_cache_info, dev)
221
222#define CACHE_ATTR(name, fmt) \
223static ssize_t name##_show(struct device *dev, \
224 struct device_attribute *attr, \
225 char *buf) \
226{ \
227 return sprintf(buf, fmt "\n", to_cache_info(dev)->cache_attrs.name);\
228} \
229DEVICE_ATTR_RO(name);
230
231CACHE_ATTR(size, "%llu")
232CACHE_ATTR(line_size, "%u")
233CACHE_ATTR(indexing, "%u")
234CACHE_ATTR(write_policy, "%u")
235
236static struct attribute *cache_attrs[] = {
237 &dev_attr_indexing.attr,
238 &dev_attr_size.attr,
239 &dev_attr_line_size.attr,
240 &dev_attr_write_policy.attr,
241 NULL,
242};
243ATTRIBUTE_GROUPS(cache);
244
245static void node_cache_release(struct device *dev)
246{
247 kfree(dev);
248}
249
250static void node_cacheinfo_release(struct device *dev)
251{
252 struct node_cache_info *info = to_cache_info(dev);
253 kfree(info);
254}
255
256static void node_init_cache_dev(struct node *node)
257{
258 struct device *dev;
259
260 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
261 if (!dev)
262 return;
263
264 dev->parent = &node->dev;
265 dev->release = node_cache_release;
266 if (dev_set_name(dev, "memory_side_cache"))
267 goto free_dev;
268
269 if (device_register(dev))
270 goto free_name;
271
272 pm_runtime_no_callbacks(dev);
273 node->cache_dev = dev;
274 return;
275free_name:
276 kfree_const(dev->kobj.name);
277free_dev:
278 kfree(dev);
279}
280
281/**
282 * node_add_cache() - add cache attribute to a memory node
283 * @nid: Node identifier that has new cache attributes
284 * @cache_attrs: Attributes for the cache being added
285 */
286void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs)
287{
288 struct node_cache_info *info;
289 struct device *dev;
290 struct node *node;
291
292 if (!node_online(nid) || !node_devices[nid])
293 return;
294
295 node = node_devices[nid];
296 list_for_each_entry(info, &node->cache_attrs, node) {
297 if (info->cache_attrs.level == cache_attrs->level) {
298 dev_warn(&node->dev,
299 "attempt to add duplicate cache level:%d\n",
300 cache_attrs->level);
301 return;
302 }
303 }
304
305 if (!node->cache_dev)
306 node_init_cache_dev(node);
307 if (!node->cache_dev)
308 return;
309
310 info = kzalloc(sizeof(*info), GFP_KERNEL);
311 if (!info)
312 return;
313
314 dev = &info->dev;
315 dev->parent = node->cache_dev;
316 dev->release = node_cacheinfo_release;
317 dev->groups = cache_groups;
318 if (dev_set_name(dev, "index%d", cache_attrs->level))
319 goto free_cache;
320
321 info->cache_attrs = *cache_attrs;
322 if (device_register(dev)) {
323 dev_warn(&node->dev, "failed to add cache level:%d\n",
324 cache_attrs->level);
325 goto free_name;
326 }
327 pm_runtime_no_callbacks(dev);
328 list_add_tail(&info->node, &node->cache_attrs);
329 return;
330free_name:
331 kfree_const(dev->kobj.name);
332free_cache:
333 kfree(info);
334}
335
336static void node_remove_caches(struct node *node)
337{
338 struct node_cache_info *info, *next;
339
340 if (!node->cache_dev)
341 return;
342
343 list_for_each_entry_safe(info, next, &node->cache_attrs, node) {
344 list_del(&info->node);
345 device_unregister(&info->dev);
346 }
347 device_unregister(node->cache_dev);
348}
349
350static void node_init_caches(unsigned int nid)
351{
352 INIT_LIST_HEAD(&node_devices[nid]->cache_attrs);
353}
354#else
355static void node_init_caches(unsigned int nid) { }
356static void node_remove_caches(struct node *node) { }
208#endif 357#endif
209 358
210#define K(x) ((x) << (PAGE_SHIFT - 10)) 359#define K(x) ((x) << (PAGE_SHIFT - 10))
@@ -489,6 +638,7 @@ void unregister_node(struct node *node)
489{ 638{
490 hugetlb_unregister_node(node); /* no-op, if memoryless node */ 639 hugetlb_unregister_node(node); /* no-op, if memoryless node */
491 node_remove_accesses(node); 640 node_remove_accesses(node);
641 node_remove_caches(node);
492 device_unregister(&node->dev); 642 device_unregister(&node->dev);
493} 643}
494 644
@@ -781,6 +931,7 @@ int __register_one_node(int nid)
781 INIT_LIST_HEAD(&node_devices[nid]->access_list); 931 INIT_LIST_HEAD(&node_devices[nid]->access_list);
782 /* initialize work queue for memory hot plug */ 932 /* initialize work queue for memory hot plug */
783 init_node_hugetlb_work(nid); 933 init_node_hugetlb_work(nid);
934 node_init_caches(nid);
784 935
785 return error; 936 return error;
786} 937}