1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-09-24     GuEe-GUI     the first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 
14 #define DBG_TAG "rtdm.numa"
15 #define DBG_LVL DBG_INFO
16 #include <rtdbg.h>
17 
18 #include <drivers/pic.h>
19 
20 struct numa_memory
21 {
22     rt_list_t list;
23 
24     int nid;
25     rt_uint64_t start;
26     rt_uint64_t end;
27 
28     union
29     {
30         void *ofw_node;
31     };
32 };
33 
34 static rt_bool_t numa_enabled = RT_FALSE;
35 static int cpu_numa_map[RT_CPUS_NR] rt_section(".bss.noclean.numa");
36 static rt_list_t numa_memory_nodes rt_section(".bss.noclean.numa");
37 
rt_numa_cpu_id(int cpuid)38 int rt_numa_cpu_id(int cpuid)
39 {
40     if (!numa_enabled)
41     {
42         return -RT_ENOSYS;
43     }
44 
45     return cpuid < RT_ARRAY_SIZE(cpu_numa_map) ? cpu_numa_map[cpuid] : -RT_EINVAL;
46 }
47 
rt_numa_device_id(struct rt_device * dev)48 int rt_numa_device_id(struct rt_device *dev)
49 {
50     rt_uint32_t nid = (rt_uint32_t)-RT_ENOSYS;
51 
52     if (!numa_enabled)
53     {
54         return nid;
55     }
56 
57     return rt_dm_dev_prop_read_u32(dev, "numa-node-id", &nid) ? : (int)nid;
58 }
59 
rt_numa_memory_affinity(rt_uint64_t phy_addr,rt_bitmap_t * out_affinity)60 rt_err_t rt_numa_memory_affinity(rt_uint64_t phy_addr, rt_bitmap_t *out_affinity)
61 {
62     struct numa_memory *nm;
63 
64     if (!out_affinity)
65     {
66         return -RT_EINVAL;
67     }
68 
69     if (!numa_enabled)
70     {
71         /* Default to CPU#0 */
72         RT_IRQ_AFFINITY_SET(out_affinity, 0);
73 
74         return RT_EOK;
75     }
76 
77     rt_memset(out_affinity, 0, sizeof(*out_affinity) * RT_BITMAP_LEN(RT_CPUS_NR));
78 
79     rt_list_for_each_entry(nm, &numa_memory_nodes, list)
80     {
81         if (phy_addr >= nm->start && phy_addr < nm->end)
82         {
83             for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i)
84             {
85                 if (cpu_numa_map[i] == nm->nid)
86                 {
87                     RT_IRQ_AFFINITY_SET(out_affinity, i);
88                 }
89             }
90 
91             return RT_EOK;
92         }
93     }
94 
95     return -RT_EEMPTY;
96 }
97 
98 #ifdef RT_USING_OFW
numa_ofw_init(void)99 static int numa_ofw_init(void)
100 {
101     int i = 0;
102     rt_uint32_t nid;
103     const char *numa_conf;
104     struct rt_ofw_node *np = RT_NULL;
105 
106     numa_conf = rt_ofw_bootargs_select("numa=", 0);
107 
108     if (!numa_conf || rt_strcmp(numa_conf, "on"))
109     {
110         return (int)RT_EOK;
111     }
112 
113     numa_enabled = RT_TRUE;
114 
115     for (int i = 0; i < RT_ARRAY_SIZE(cpu_numa_map); ++i)
116     {
117         cpu_numa_map[i] = -RT_ENOSYS;
118     }
119 
120     rt_list_init(&numa_memory_nodes);
121 
122     rt_ofw_foreach_cpu_node(np)
123     {
124         rt_ofw_prop_read_u32(np, "numa-node-id", (rt_uint32_t *)&cpu_numa_map[i]);
125 
126         if (++i >= RT_CPUS_NR)
127         {
128             break;
129         }
130     }
131 
132     rt_ofw_foreach_node_by_type(np, "memory")
133     {
134         if (!rt_ofw_prop_read_u32(np, "numa-node-id", &nid))
135         {
136             int mem_nr = rt_ofw_get_address_count(np);
137 
138             for (i = 0; i < mem_nr; ++i)
139             {
140                 rt_uint64_t addr, size;
141                 struct numa_memory *nm;
142 
143                 if (rt_ofw_get_address(np, i, &addr, &size))
144                 {
145                     continue;
146                 }
147 
148                 nm = rt_malloc(sizeof(*nm));
149 
150                 if (!nm)
151                 {
152                     LOG_E("No memory to record NUMA[%d] memory[%p, %p] info",
153                             nid, addr, addr + size);
154 
155                     return (int)-RT_ENOMEM;
156                 }
157 
158                 nm->start = addr;
159                 nm->end = addr + size;
160                 nm->ofw_node = np;
161 
162                 rt_list_init(&nm->list);
163                 rt_list_insert_before(&numa_memory_nodes, &nm->list);
164             }
165         }
166     }
167 
168     return 0;
169 }
170 INIT_CORE_EXPORT(numa_ofw_init);
171 #endif /* RT_USING_OFW */
172