~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/mm/drmem.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Dynamic reconfiguration memory support
  4  *
  5  * Copyright 2017 IBM Corporation
  6  */
  7 
  8 #define pr_fmt(fmt) "drmem: " fmt
  9 
 10 #include <linux/kernel.h>
 11 #include <linux/of.h>
 12 #include <linux/of_fdt.h>
 13 #include <linux/memblock.h>
 14 #include <linux/slab.h>
 15 #include <asm/drmem.h>
 16 
 17 static int n_root_addr_cells, n_root_size_cells;
 18 
 19 static struct drmem_lmb_info __drmem_info;
 20 struct drmem_lmb_info *drmem_info = &__drmem_info;
 21 static bool in_drmem_update;
 22 
 23 u64 drmem_lmb_memory_max(void)
 24 {
 25         struct drmem_lmb *last_lmb;
 26 
 27         last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
 28         return last_lmb->base_addr + drmem_lmb_size();
 29 }
 30 
 31 static u32 drmem_lmb_flags(struct drmem_lmb *lmb)
 32 {
 33         /*
 34          * Return the value of the lmb flags field minus the reserved
 35          * bit used internally for hotplug processing.
 36          */
 37         return lmb->flags & ~DRMEM_LMB_RESERVED;
 38 }
 39 
 40 static struct property *clone_property(struct property *prop, u32 prop_sz)
 41 {
 42         struct property *new_prop;
 43 
 44         new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
 45         if (!new_prop)
 46                 return NULL;
 47 
 48         new_prop->name = kstrdup(prop->name, GFP_KERNEL);
 49         new_prop->value = kzalloc(prop_sz, GFP_KERNEL);
 50         if (!new_prop->name || !new_prop->value) {
 51                 kfree(new_prop->name);
 52                 kfree(new_prop->value);
 53                 kfree(new_prop);
 54                 return NULL;
 55         }
 56 
 57         new_prop->length = prop_sz;
 58 #if defined(CONFIG_OF_DYNAMIC)
 59         of_property_set_flag(new_prop, OF_DYNAMIC);
 60 #endif
 61         return new_prop;
 62 }
 63 
 64 static int drmem_update_dt_v1(struct device_node *memory,
 65                               struct property *prop)
 66 {
 67         struct property *new_prop;
 68         struct of_drconf_cell_v1 *dr_cell;
 69         struct drmem_lmb *lmb;
 70         __be32 *p;
 71 
 72         new_prop = clone_property(prop, prop->length);
 73         if (!new_prop)
 74                 return -1;
 75 
 76         p = new_prop->value;
 77         *p++ = cpu_to_be32(drmem_info->n_lmbs);
 78 
 79         dr_cell = (struct of_drconf_cell_v1 *)p;
 80 
 81         for_each_drmem_lmb(lmb) {
 82                 dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
 83                 dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
 84                 dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
 85                 dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
 86 
 87                 dr_cell++;
 88         }
 89 
 90         of_update_property(memory, new_prop);
 91         return 0;
 92 }
 93 
 94 static void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
 95                                 struct drmem_lmb *lmb)
 96 {
 97         dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
 98         dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
 99         dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
100         dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
101 }
102 
103 static int drmem_update_dt_v2(struct device_node *memory,
104                               struct property *prop)
105 {
106         struct property *new_prop;
107         struct of_drconf_cell_v2 *dr_cell;
108         struct drmem_lmb *lmb, *prev_lmb;
109         u32 lmb_sets, prop_sz, seq_lmbs;
110         u32 *p;
111 
112         /* First pass, determine how many LMB sets are needed. */
113         lmb_sets = 0;
114         prev_lmb = NULL;
115         for_each_drmem_lmb(lmb) {
116                 if (!prev_lmb) {
117                         prev_lmb = lmb;
118                         lmb_sets++;
119                         continue;
120                 }
121 
122                 if (prev_lmb->aa_index != lmb->aa_index ||
123                     drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
124                         lmb_sets++;
125 
126                 prev_lmb = lmb;
127         }
128 
129         prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
130         new_prop = clone_property(prop, prop_sz);
131         if (!new_prop)
132                 return -1;
133 
134         p = new_prop->value;
135         *p++ = cpu_to_be32(lmb_sets);
136 
137         dr_cell = (struct of_drconf_cell_v2 *)p;
138 
139         /* Second pass, populate the LMB set data */
140         prev_lmb = NULL;
141         seq_lmbs = 0;
142         for_each_drmem_lmb(lmb) {
143                 if (prev_lmb == NULL) {
144                         /* Start of first LMB set */
145                         prev_lmb = lmb;
146                         init_drconf_v2_cell(dr_cell, lmb);
147                         seq_lmbs++;
148                         continue;
149                 }
150 
151                 if (prev_lmb->aa_index != lmb->aa_index ||
152                     drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb)) {
153                         /* end of one set, start of another */
154                         dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
155                         dr_cell++;
156 
157                         init_drconf_v2_cell(dr_cell, lmb);
158                         seq_lmbs = 1;
159                 } else {
160                         seq_lmbs++;
161                 }
162 
163                 prev_lmb = lmb;
164         }
165 
166         /* close out last LMB set */
167         dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
168         of_update_property(memory, new_prop);
169         return 0;
170 }
171 
172 int drmem_update_dt(void)
173 {
174         struct device_node *memory;
175         struct property *prop;
176         int rc = -1;
177 
178         memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
179         if (!memory)
180                 return -1;
181 
182         /*
183          * Set in_drmem_update to prevent the notifier callback to process the
184          * DT property back since the change is coming from the LMB tree.
185          */
186         in_drmem_update = true;
187         prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
188         if (prop) {
189                 rc = drmem_update_dt_v1(memory, prop);
190         } else {
191                 prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
192                 if (prop)
193                         rc = drmem_update_dt_v2(memory, prop);
194         }
195         in_drmem_update = false;
196 
197         of_node_put(memory);
198         return rc;
199 }
200 
201 static void read_drconf_v1_cell(struct drmem_lmb *lmb,
202                                        const __be32 **prop)
203 {
204         const __be32 *p = *prop;
205 
206         lmb->base_addr = of_read_number(p, n_root_addr_cells);
207         p += n_root_addr_cells;
208         lmb->drc_index = of_read_number(p++, 1);
209 
210         p++; /* skip reserved field */
211 
212         lmb->aa_index = of_read_number(p++, 1);
213         lmb->flags = of_read_number(p++, 1);
214 
215         *prop = p;
216 }
217 
218 static int
219 __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
220                      int (*func)(struct drmem_lmb *, const __be32 **, void *))
221 {
222         struct drmem_lmb lmb;
223         u32 i, n_lmbs;
224         int ret = 0;
225 
226         n_lmbs = of_read_number(prop++, 1);
227         for (i = 0; i < n_lmbs; i++) {
228                 read_drconf_v1_cell(&lmb, &prop);
229                 ret = func(&lmb, &usm, data);
230                 if (ret)
231                         break;
232         }
233 
234         return ret;
235 }
236 
237 static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
238                                        const __be32 **prop)
239 {
240         const __be32 *p = *prop;
241 
242         dr_cell->seq_lmbs = of_read_number(p++, 1);
243         dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
244         p += n_root_addr_cells;
245         dr_cell->drc_index = of_read_number(p++, 1);
246         dr_cell->aa_index = of_read_number(p++, 1);
247         dr_cell->flags = of_read_number(p++, 1);
248 
249         *prop = p;
250 }
251 
252 static int
253 __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
254                      int (*func)(struct drmem_lmb *, const __be32 **, void *))
255 {
256         struct of_drconf_cell_v2 dr_cell;
257         struct drmem_lmb lmb;
258         u32 i, j, lmb_sets;
259         int ret = 0;
260 
261         lmb_sets = of_read_number(prop++, 1);
262         for (i = 0; i < lmb_sets; i++) {
263                 read_drconf_v2_cell(&dr_cell, &prop);
264 
265                 for (j = 0; j < dr_cell.seq_lmbs; j++) {
266                         lmb.base_addr = dr_cell.base_addr;
267                         dr_cell.base_addr += drmem_lmb_size();
268 
269                         lmb.drc_index = dr_cell.drc_index;
270                         dr_cell.drc_index++;
271 
272                         lmb.aa_index = dr_cell.aa_index;
273                         lmb.flags = dr_cell.flags;
274 
275                         ret = func(&lmb, &usm, data);
276                         if (ret)
277                                 break;
278                 }
279         }
280 
281         return ret;
282 }
283 
284 #ifdef CONFIG_PPC_PSERIES
285 int __init walk_drmem_lmbs_early(unsigned long node, void *data,
286                 int (*func)(struct drmem_lmb *, const __be32 **, void *))
287 {
288         const __be32 *prop, *usm;
289         int len, ret = -ENODEV;
290 
291         prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
292         if (!prop || len < dt_root_size_cells * sizeof(__be32))
293                 return ret;
294 
295         /* Get the address & size cells */
296         n_root_addr_cells = dt_root_addr_cells;
297         n_root_size_cells = dt_root_size_cells;
298 
299         drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
300 
301         usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
302 
303         prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
304         if (prop) {
305                 ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
306         } else {
307                 prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
308                                            &len);
309                 if (prop)
310                         ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
311         }
312 
313         memblock_dump_all();
314         return ret;
315 }
316 
317 /*
318  * Update the LMB associativity index.
319  */
320 static int update_lmb(struct drmem_lmb *updated_lmb,
321                       __maybe_unused const __be32 **usm,
322                       __maybe_unused void *data)
323 {
324         struct drmem_lmb *lmb;
325 
326         for_each_drmem_lmb(lmb) {
327                 if (lmb->drc_index != updated_lmb->drc_index)
328                         continue;
329 
330                 lmb->aa_index = updated_lmb->aa_index;
331                 break;
332         }
333         return 0;
334 }
335 
336 /*
337  * Update the LMB associativity index.
338  *
339  * This needs to be called when the hypervisor is updating the
340  * dynamic-reconfiguration-memory node property.
341  */
342 void drmem_update_lmbs(struct property *prop)
343 {
344         /*
345          * Don't update the LMBs if triggered by the update done in
346          * drmem_update_dt(), the LMB values have been used to the update the DT
347          * property in that case.
348          */
349         if (in_drmem_update)
350                 return;
351         if (!strcmp(prop->name, "ibm,dynamic-memory"))
352                 __walk_drmem_v1_lmbs(prop->value, NULL, NULL, update_lmb);
353         else if (!strcmp(prop->name, "ibm,dynamic-memory-v2"))
354                 __walk_drmem_v2_lmbs(prop->value, NULL, NULL, update_lmb);
355 }
356 #endif
357 
358 static int init_drmem_lmb_size(struct device_node *dn)
359 {
360         const __be32 *prop;
361         int len;
362 
363         if (drmem_info->lmb_size)
364                 return 0;
365 
366         prop = of_get_property(dn, "ibm,lmb-size", &len);
367         if (!prop || len < n_root_size_cells * sizeof(__be32)) {
368                 pr_info("Could not determine LMB size\n");
369                 return -1;
370         }
371 
372         drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
373         return 0;
374 }
375 
376 /*
377  * Returns the property linux,drconf-usable-memory if
378  * it exists (the property exists only in kexec/kdump kernels,
379  * added by kexec-tools)
380  */
381 static const __be32 *of_get_usable_memory(struct device_node *dn)
382 {
383         const __be32 *prop;
384         u32 len;
385 
386         prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
387         if (!prop || len < sizeof(unsigned int))
388                 return NULL;
389 
390         return prop;
391 }
392 
393 int walk_drmem_lmbs(struct device_node *dn, void *data,
394                     int (*func)(struct drmem_lmb *, const __be32 **, void *))
395 {
396         struct device_node *root = of_find_node_by_path("/");
397         const __be32 *prop, *usm;
398         int ret = -ENODEV;
399 
400         if (!root)
401                 return ret;
402 
403         /* Get the address & size cells */
404         n_root_addr_cells = of_n_addr_cells(root);
405         n_root_size_cells = of_n_size_cells(root);
406         of_node_put(root);
407 
408         if (init_drmem_lmb_size(dn))
409                 return ret;
410 
411         usm = of_get_usable_memory(dn);
412 
413         prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
414         if (prop) {
415                 ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
416         } else {
417                 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
418                 if (prop)
419                         ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
420         }
421 
422         return ret;
423 }
424 
425 static void __init init_drmem_v1_lmbs(const __be32 *prop)
426 {
427         struct drmem_lmb *lmb;
428 
429         drmem_info->n_lmbs = of_read_number(prop++, 1);
430         if (drmem_info->n_lmbs == 0)
431                 return;
432 
433         drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
434                                    GFP_KERNEL);
435         if (!drmem_info->lmbs)
436                 return;
437 
438         for_each_drmem_lmb(lmb)
439                 read_drconf_v1_cell(lmb, &prop);
440 }
441 
442 static void __init init_drmem_v2_lmbs(const __be32 *prop)
443 {
444         struct drmem_lmb *lmb;
445         struct of_drconf_cell_v2 dr_cell;
446         const __be32 *p;
447         u32 i, j, lmb_sets;
448         int lmb_index;
449 
450         lmb_sets = of_read_number(prop++, 1);
451         if (lmb_sets == 0)
452                 return;
453 
454         /* first pass, calculate the number of LMBs */
455         p = prop;
456         for (i = 0; i < lmb_sets; i++) {
457                 read_drconf_v2_cell(&dr_cell, &p);
458                 drmem_info->n_lmbs += dr_cell.seq_lmbs;
459         }
460 
461         drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
462                                    GFP_KERNEL);
463         if (!drmem_info->lmbs)
464                 return;
465 
466         /* second pass, read in the LMB information */
467         lmb_index = 0;
468         p = prop;
469 
470         for (i = 0; i < lmb_sets; i++) {
471                 read_drconf_v2_cell(&dr_cell, &p);
472 
473                 for (j = 0; j < dr_cell.seq_lmbs; j++) {
474                         lmb = &drmem_info->lmbs[lmb_index++];
475 
476                         lmb->base_addr = dr_cell.base_addr;
477                         dr_cell.base_addr += drmem_info->lmb_size;
478 
479                         lmb->drc_index = dr_cell.drc_index;
480                         dr_cell.drc_index++;
481 
482                         lmb->aa_index = dr_cell.aa_index;
483                         lmb->flags = dr_cell.flags;
484                 }
485         }
486 }
487 
488 static int __init drmem_init(void)
489 {
490         struct device_node *dn;
491         const __be32 *prop;
492 
493         dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
494         if (!dn)
495                 return 0;
496 
497         if (init_drmem_lmb_size(dn)) {
498                 of_node_put(dn);
499                 return 0;
500         }
501 
502         prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
503         if (prop) {
504                 init_drmem_v1_lmbs(prop);
505         } else {
506                 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
507                 if (prop)
508                         init_drmem_v2_lmbs(prop);
509         }
510 
511         of_node_put(dn);
512         return 0;
513 }
514 late_initcall(drmem_init);
515 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php