98b076e09f5fad07ee1732e748c7898657790255
[dpdk.git] / drivers / common / dpaax / dpaax_iova_table.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018 NXP
3  */
4
5 #include <rte_memory.h>
6
7 #include "dpaax_iova_table.h"
8 #include "dpaax_logs.h"
9
10 /* Global dpaax logger identifier */
11 int dpaax_logger;
12
13 /* Global table reference */
14 struct dpaax_iova_table *dpaax_iova_table_p;
15
16 static int dpaax_handle_memevents(void);
17
18 /* A structure representing the device-tree node available in /proc/device-tree.
19  */
20 struct reg_node {
21         phys_addr_t addr;
22         size_t len;
23 };
24
25 /* A ntohll equivalent routine
26  * XXX: This is only applicable for 64 bit environment.
27  */
28 static void
29 rotate_8(unsigned char *arr)
30 {
31         uint32_t temp;
32         uint32_t *first_half;
33         uint32_t *second_half;
34
35         first_half = (uint32_t *)(arr);
36         second_half = (uint32_t *)(arr + 4);
37
38         temp = *first_half;
39         *first_half = *second_half;
40         *second_half = temp;
41
42         *first_half = ntohl(*first_half);
43         *second_half = ntohl(*second_half);
44 }
45
46 /* read_memory_nodes
47  * Memory layout for DPAAx platforms (LS1043, LS1046, LS1088, LS2088, LX2160)
48  * are populated by Uboot and available in device tree:
49  * /proc/device-tree/memory@<address>/reg <= register.
50  * Entries are of the form:
51  *  (<8 byte start addr><8 byte length>)(..more similar blocks of start,len>)..
52  *
53  * @param count
54  *    OUT populate number of entries found in memory node
55  * @return
56  *    Pointer to array of reg_node elements, count size
57  */
58 static struct reg_node *
59 read_memory_node(unsigned int *count)
60 {
61         int fd, ret, i;
62         unsigned int j;
63         glob_t result = {0};
64         struct stat statbuf = {0};
65         char file_data[MEM_NODE_FILE_LEN];
66         struct reg_node *nodes = NULL;
67
68         *count = 0;
69
70         ret = glob(MEM_NODE_PATH_GLOB, 0, NULL, &result);
71         if (ret != 0)
72                 ret = glob(MEM_NODE_PATH_GLOB_VM, 0, NULL, &result);
73
74         if (ret != 0) {
75                 DPAAX_DEBUG("Unable to glob device-tree memory node (err: %d)",
76                         ret);
77                 goto out;
78         }
79
80         if (result.gl_pathc != 1) {
81                 /* Either more than one memory@<addr> node found, or none.
82                  * In either case, cannot work ahead.
83                  */
84                 DPAAX_DEBUG("Found (%zu) entries in device-tree. Not supported!",
85                             result.gl_pathc);
86                 goto out;
87         }
88
89         DPAAX_DEBUG("Opening and parsing device-tree node: (%s)",
90                     result.gl_pathv[0]);
91         fd = open(result.gl_pathv[0], O_RDONLY);
92         if (fd < 0) {
93                 DPAAX_DEBUG("Unable to open the device-tree node: (%s)(fd=%d)",
94                             MEM_NODE_PATH_GLOB, fd);
95                 goto cleanup;
96         }
97
98         /* Stat to get the file size */
99         ret = fstat(fd, &statbuf);
100         if (ret != 0) {
101                 DPAAX_DEBUG("Unable to get device-tree memory node size.");
102                 goto cleanup;
103         }
104
105         DPAAX_DEBUG("Size of device-tree mem node: %" PRIu64, statbuf.st_size);
106         if (statbuf.st_size > MEM_NODE_FILE_LEN) {
107                 DPAAX_DEBUG("More memory nodes available than assumed.");
108                 DPAAX_DEBUG("System may not work properly!");
109         }
110
111         ret = read(fd, file_data, statbuf.st_size > MEM_NODE_FILE_LEN ?
112                                   MEM_NODE_FILE_LEN : statbuf.st_size);
113         if (ret <= 0) {
114                 DPAAX_DEBUG("Unable to read device-tree memory node: (%d)",
115                             ret);
116                 goto cleanup;
117         }
118
119         /* The reg node should be multiple of 16 bytes, 8 bytes each for addr
120          * and len.
121          */
122         *count = (statbuf.st_size / 16);
123         if ((*count) <= 0 || (statbuf.st_size % 16 != 0)) {
124                 DPAAX_DEBUG("Invalid memory node values or count. (size=%" PRIu64 ")",
125                             statbuf.st_size);
126                 goto cleanup;
127         }
128
129         /* each entry is of 16 bytes, and size/16 is total count of entries */
130         nodes = malloc(sizeof(struct reg_node) * (*count));
131         if (!nodes) {
132                 DPAAX_DEBUG("Failure in allocating working memory.");
133                 goto cleanup;
134         }
135         memset(nodes, 0, sizeof(struct reg_node) * (*count));
136
137         for (i = 0, j = 0; i < (statbuf.st_size) && j < (*count); i += 16, j++) {
138                 memcpy(&nodes[j], file_data + i, 16);
139                 /* Rotate (ntohl) each 8 byte entry */
140                 rotate_8((unsigned char *)(&(nodes[j].addr)));
141                 rotate_8((unsigned char *)(&(nodes[j].len)));
142         }
143
144         DPAAX_DEBUG("Device-tree memory node data:");
145         do {
146                 DPAAX_DEBUG("    %08" PRIx64 " %08zu",
147                             nodes[j].addr, nodes[j].len);
148         } while (--j);
149
150 cleanup:
151         close(fd);
152         globfree(&result);
153 out:
154         return nodes;
155 }
156
157 int
158 dpaax_iova_table_populate(void)
159 {
160         int ret;
161         unsigned int i, node_count;
162         size_t tot_memory_size, total_table_size;
163         struct reg_node *nodes;
164         struct dpaax_iovat_element *entry;
165
166         /* dpaax_iova_table_p is a singleton - only one instance should be
167          * created.
168          */
169         if (dpaax_iova_table_p) {
170                 DPAAX_DEBUG("Multiple allocation attempt for IOVA Table (%p)",
171                             dpaax_iova_table_p);
172                 /* This can be an error case as well - some path not cleaning
173                  * up table - but, for now, it is assumed that if IOVA Table
174                  * pointer is valid, table is allocated.
175                  */
176                 return 0;
177         }
178
179         nodes = read_memory_node(&node_count);
180         if (nodes == NULL) {
181                 DPAAX_WARN("PA->VA translation not available;");
182                 DPAAX_WARN("Expect performance impact.");
183                 return -1;
184         }
185
186         tot_memory_size = 0;
187         for (i = 0; i < node_count; i++)
188                 tot_memory_size += nodes[i].len;
189
190         DPAAX_DEBUG("Total available PA memory size: %zu", tot_memory_size);
191
192         /* Total table size = meta data + tot_memory_size/8 */
193         total_table_size = sizeof(struct dpaax_iova_table) +
194                            (sizeof(struct dpaax_iovat_element) * node_count) +
195                            ((tot_memory_size / DPAAX_MEM_SPLIT) * sizeof(uint64_t));
196
197         /* TODO: This memory doesn't need to shared but needs to be always
198          * pinned to RAM (no swap out) - using hugepage rather than malloc
199          */
200         dpaax_iova_table_p = rte_zmalloc(NULL, total_table_size, 0);
201         if (dpaax_iova_table_p == NULL) {
202                 DPAAX_WARN("Unable to allocate memory for PA->VA Table;");
203                 DPAAX_WARN("PA->VA translation not available;");
204                 DPAAX_WARN("Expect performance impact.");
205                 free(nodes);
206                 return -1;
207         }
208
209         /* Initialize table */
210         dpaax_iova_table_p->count = node_count;
211         entry = dpaax_iova_table_p->entries;
212
213         DPAAX_DEBUG("IOVA Table entries: (entry start = %p)", (void *)entry);
214         DPAAX_DEBUG("\t(entry),(start),(len),(next)");
215
216         for (i = 0; i < node_count; i++) {
217                 /* dpaax_iova_table_p
218                  * |   dpaax_iova_table_p->entries
219                  * |      |
220                  * |      |
221                  * V      V
222                  * +------+------+-------+---+----------+---------+---
223                  * |iova_ |entry | entry |   | pages    | pages   |
224                  * |table | 1    |  2    |...| entry 1  | entry2  |
225                  * +-----'+.-----+-------+---+;---------+;--------+---
226                  *         \      \          /          /
227                  *          `~~~~~~|~~~~~>pages        /
228                  *                  \                 /
229                  *                   `~~~~~~~~~~~>pages
230                  */
231                 entry[i].start = nodes[i].addr;
232                 entry[i].len = nodes[i].len;
233                 if (i > 0)
234                         entry[i].pages = entry[i-1].pages +
235                                 ((entry[i-1].len/DPAAX_MEM_SPLIT));
236                 else
237                         entry[i].pages = (uint64_t *)((unsigned char *)entry +
238                                          (sizeof(struct dpaax_iovat_element) *
239                                          node_count));
240
241                 DPAAX_DEBUG("\t(%u),(%8"PRIx64"),(%8zu),(%8p)",
242                             i, entry[i].start, entry[i].len, entry[i].pages);
243         }
244
245         /* Release memory associated with nodes array - not required now */
246         free(nodes);
247
248         DPAAX_DEBUG("Adding mem-event handler");
249         ret = dpaax_handle_memevents();
250         if (ret) {
251                 DPAAX_ERR("Unable to add mem-event handler");
252                 DPAAX_WARN("Cases with non-buffer pool mem won't work!");
253         }
254
255         return 0;
256 }
257
258 void
259 dpaax_iova_table_depopulate(void)
260 {
261         if (dpaax_iova_table_p == NULL)
262                 return;
263
264         rte_free(dpaax_iova_table_p->entries);
265         dpaax_iova_table_p = NULL;
266
267         DPAAX_DEBUG("IOVA Table cleanedup");
268 }
269
270 int
271 dpaax_iova_table_update(phys_addr_t paddr, void *vaddr, size_t length)
272 {
273         int found = 0;
274         unsigned int i;
275         size_t req_length = length, e_offset;
276         struct dpaax_iovat_element *entry;
277         uintptr_t align_vaddr;
278         phys_addr_t align_paddr;
279
280         if (unlikely(dpaax_iova_table_p == NULL))
281                 return -1;
282
283         align_paddr = paddr & DPAAX_MEM_SPLIT_MASK;
284         align_vaddr = ((uintptr_t)vaddr & DPAAX_MEM_SPLIT_MASK);
285
286         /* Check if paddr is available in table */
287         entry = dpaax_iova_table_p->entries;
288         for (i = 0; i < dpaax_iova_table_p->count; i++) {
289                 if (align_paddr < entry[i].start) {
290                         /* Address lower than start, but not found in previous
291                          * iteration shouldn't exist.
292                          */
293                         DPAAX_ERR("Add: Incorrect entry for PA->VA Table"
294                                   "(%"PRIu64")", paddr);
295                         DPAAX_ERR("Add: Lowest address: %"PRIu64"",
296                                   entry[i].start);
297                         return -1;
298                 }
299
300                 if (align_paddr > (entry[i].start + entry[i].len))
301                         continue;
302
303                 /* align_paddr >= start && align_paddr < (start + len) */
304                 found = 1;
305
306                 do {
307                         e_offset = ((align_paddr - entry[i].start) / DPAAX_MEM_SPLIT);
308                         /* TODO: Whatif something already exists at this
309                          * location - is that an error? For now, ignoring the
310                          * case.
311                          */
312                         entry[i].pages[e_offset] = align_vaddr;
313 #ifdef RTE_COMMON_DPAAX_DEBUG
314                         DPAAX_DEBUG("Added: vaddr=%zu for Phy:%"PRIu64" at %zu"
315                                     " remaining len %zu", align_vaddr,
316                                     align_paddr, e_offset, req_length);
317 #endif
318                         /* Incoming request can be larger than the
319                          * DPAAX_MEM_SPLIT size - in which case, multiple
320                          * entries in entry->pages[] are filled up.
321                          */
322                         if (req_length <= DPAAX_MEM_SPLIT)
323                                 break;
324                         align_paddr += DPAAX_MEM_SPLIT;
325                         align_vaddr += DPAAX_MEM_SPLIT;
326                         req_length -= DPAAX_MEM_SPLIT;
327                 } while (1);
328
329                 break;
330         }
331
332         if (!found) {
333                 /* There might be case where the incoming physical address is
334                  * beyond the address discovered in the memory node of
335                  * device-tree. Specially if some malloc'd area is used by EAL
336                  * and the memevent handlers passes that across. But, this is
337                  * not necessarily an error.
338                  */
339                 DPAAX_DEBUG("Add: Unable to find slot for vaddr:(%p),"
340                             " phy(%"PRIu64")",
341                             vaddr, paddr);
342                 return -1;
343         }
344 #ifdef RTE_COMMON_DPAAX_DEBUG
345         DPAAX_DEBUG("Add: Found slot at (%"PRIu64")[(%zu)] for vaddr:(%p),"
346                     " phy(%"PRIu64"), len(%zu)", entry[i].start, e_offset,
347                     vaddr, paddr, length);
348 #endif
349         return 0;
350 }
351
352 /* dpaax_iova_table_dump
353  * Dump the table, with its entries, on screen. Only works in Debug Mode
354  * Not for weak hearted - the tables can get quite large
355  */
356 void
357 dpaax_iova_table_dump(void)
358 {
359         unsigned int i, j;
360         struct dpaax_iovat_element *entry;
361
362         /* In case DEBUG is not enabled, some 'if' conditions might misbehave
363          * as they have nothing else in them  except a DPAAX_DEBUG() which if
364          * tuned out would leave 'if' naked.
365          */
366         if (rte_log_get_global_level() < RTE_LOG_DEBUG) {
367                 DPAAX_ERR("Set log level to Debug for PA->Table dump!");
368                 return;
369         }
370
371         DPAAX_DEBUG(" === Start of PA->VA Translation Table ===");
372         if (dpaax_iova_table_p == NULL)
373                 DPAAX_DEBUG("\tNULL");
374
375         entry = dpaax_iova_table_p->entries;
376         for (i = 0; i < dpaax_iova_table_p->count; i++) {
377                 DPAAX_DEBUG("\t(%16i),(%16"PRIu64"),(%16zu),(%16p)",
378                             i, entry[i].start, entry[i].len, entry[i].pages);
379                 DPAAX_DEBUG("\t\t          (PA),          (VA)");
380                 for (j = 0; j < (entry->len/DPAAX_MEM_SPLIT); j++) {
381                         if (entry[i].pages[j] == 0)
382                                 continue;
383                         DPAAX_DEBUG("\t\t(%16"PRIx64"),(%16"PRIx64")",
384                                     (entry[i].start + (j * sizeof(uint64_t))),
385                                     entry[i].pages[j]);
386                 }
387         }
388         DPAAX_DEBUG(" === End of PA->VA Translation Table ===");
389 }
390
391 static void
392 dpaax_memevent_cb(enum rte_mem_event type, const void *addr, size_t len,
393                   void *arg __rte_unused)
394 {
395         struct rte_memseg_list *msl;
396         struct rte_memseg *ms;
397         size_t cur_len = 0, map_len = 0;
398         phys_addr_t phys_addr;
399         void *virt_addr;
400         int ret;
401
402         DPAAX_DEBUG("Called with addr=%p, len=%zu", addr, len);
403
404         msl = rte_mem_virt2memseg_list(addr);
405
406         while (cur_len < len) {
407                 const void *va = RTE_PTR_ADD(addr, cur_len);
408
409                 ms = rte_mem_virt2memseg(va, msl);
410                 phys_addr = rte_mem_virt2phy(ms->addr);
411                 virt_addr = ms->addr;
412                 map_len = ms->len;
413 #ifdef RTE_COMMON_DPAAX_DEBUG
414                 DPAAX_DEBUG("Request for %s, va=%p, virt_addr=%p,"
415                             "iova=%"PRIu64", map_len=%zu",
416                             type == RTE_MEM_EVENT_ALLOC ?
417                             "alloc" : "dealloc",
418                             va, virt_addr, phys_addr, map_len);
419 #endif
420                 if (type == RTE_MEM_EVENT_ALLOC)
421                         ret = dpaax_iova_table_update(phys_addr, virt_addr,
422                                                       map_len);
423                 else
424                         /* In case of mem_events for MEM_EVENT_FREE, complete
425                          * hugepage is released and its PA entry is set to 0.
426                          */
427                         ret = dpaax_iova_table_update(phys_addr, 0, map_len);
428
429                 if (ret != 0) {
430                         DPAAX_DEBUG("PA-Table entry update failed. "
431                                     "Map=%d, addr=%p, len=%zu, err:(%d)",
432                                     type, va, map_len, ret);
433                         return;
434                 }
435
436                 cur_len += map_len;
437         }
438 }
439
440 static int
441 dpaax_memevent_walk_memsegs(const struct rte_memseg_list *msl __rte_unused,
442                             const struct rte_memseg *ms, size_t len,
443                             void *arg __rte_unused)
444 {
445         DPAAX_DEBUG("Walking for %p (pa=%"PRIu64") and len %zu",
446                     ms->addr, ms->phys_addr, len);
447         dpaax_iova_table_update(rte_mem_virt2phy(ms->addr), ms->addr, len);
448         return 0;
449 }
450
451 static int
452 dpaax_handle_memevents(void)
453 {
454         /* First, walk through all memsegs and pin them, before installing
455          * handler. This assures that all memseg which have already been
456          * identified/allocated by EAL, are already part of PA->VA Table. This
457          * is especially for cases where application allocates memory before
458          * the EAL or this is an externally allocated memory passed to EAL.
459          */
460         rte_memseg_contig_walk_thread_unsafe(dpaax_memevent_walk_memsegs, NULL);
461
462         return rte_mem_event_callback_register("dpaax_memevents_cb",
463                                                dpaax_memevent_cb, NULL);
464 }
465
466 RTE_INIT(dpaax_log)
467 {
468         dpaax_logger = rte_log_register("pmd.common.dpaax");
469         if (dpaax_logger >= 0)
470                 rte_log_set_level(dpaax_logger, RTE_LOG_ERR);
471 }