mem: allow querying offset into segment fd
[dpdk.git] / lib / librte_eal / common / eal_common_memory.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <errno.h>
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <inttypes.h>
13 #include <sys/mman.h>
14 #include <sys/queue.h>
15
16 #include <rte_fbarray.h>
17 #include <rte_memory.h>
18 #include <rte_eal.h>
19 #include <rte_eal_memconfig.h>
20 #include <rte_errno.h>
21 #include <rte_log.h>
22
23 #include "eal_memalloc.h"
24 #include "eal_private.h"
25 #include "eal_internal_cfg.h"
26
27 /*
28  * Try to mmap *size bytes in /dev/zero. If it is successful, return the
29  * pointer to the mmap'd area and keep *size unmodified. Else, retry
30  * with a smaller zone: decrease *size by hugepage_sz until it reaches
31  * 0. In this case, return NULL. Note: this function returns an address
32  * which is a multiple of hugepage size.
33  */
34
35 #define MEMSEG_LIST_FMT "memseg-%" PRIu64 "k-%i-%i"
36
37 static void *next_baseaddr;
38 static uint64_t system_page_sz;
39
40 void *
41 eal_get_virtual_area(void *requested_addr, size_t *size,
42                 size_t page_sz, int flags, int mmap_flags)
43 {
44         bool addr_is_hint, allow_shrink, unmap, no_align;
45         uint64_t map_sz;
46         void *mapped_addr, *aligned_addr;
47
48         if (system_page_sz == 0)
49                 system_page_sz = sysconf(_SC_PAGESIZE);
50
51         mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
52
53         RTE_LOG(DEBUG, EAL, "Ask a virtual area of 0x%zx bytes\n", *size);
54
55         addr_is_hint = (flags & EAL_VIRTUAL_AREA_ADDR_IS_HINT) > 0;
56         allow_shrink = (flags & EAL_VIRTUAL_AREA_ALLOW_SHRINK) > 0;
57         unmap = (flags & EAL_VIRTUAL_AREA_UNMAP) > 0;
58
59         if (next_baseaddr == NULL && internal_config.base_virtaddr != 0 &&
60                         rte_eal_process_type() == RTE_PROC_PRIMARY)
61                 next_baseaddr = (void *) internal_config.base_virtaddr;
62
63         if (requested_addr == NULL && next_baseaddr != NULL) {
64                 requested_addr = next_baseaddr;
65                 requested_addr = RTE_PTR_ALIGN(requested_addr, page_sz);
66                 addr_is_hint = true;
67         }
68
69         /* we don't need alignment of resulting pointer in the following cases:
70          *
71          * 1. page size is equal to system size
72          * 2. we have a requested address, and it is page-aligned, and we will
73          *    be discarding the address if we get a different one.
74          *
75          * for all other cases, alignment is potentially necessary.
76          */
77         no_align = (requested_addr != NULL &&
78                 requested_addr == RTE_PTR_ALIGN(requested_addr, page_sz) &&
79                 !addr_is_hint) ||
80                 page_sz == system_page_sz;
81
82         do {
83                 map_sz = no_align ? *size : *size + page_sz;
84                 if (map_sz > SIZE_MAX) {
85                         RTE_LOG(ERR, EAL, "Map size too big\n");
86                         rte_errno = E2BIG;
87                         return NULL;
88                 }
89
90                 mapped_addr = mmap(requested_addr, (size_t)map_sz, PROT_READ,
91                                 mmap_flags, -1, 0);
92                 if (mapped_addr == MAP_FAILED && allow_shrink)
93                         *size -= page_sz;
94         } while (allow_shrink && mapped_addr == MAP_FAILED && *size > 0);
95
96         /* align resulting address - if map failed, we will ignore the value
97          * anyway, so no need to add additional checks.
98          */
99         aligned_addr = no_align ? mapped_addr :
100                         RTE_PTR_ALIGN(mapped_addr, page_sz);
101
102         if (*size == 0) {
103                 RTE_LOG(ERR, EAL, "Cannot get a virtual area of any size: %s\n",
104                         strerror(errno));
105                 rte_errno = errno;
106                 return NULL;
107         } else if (mapped_addr == MAP_FAILED) {
108                 RTE_LOG(ERR, EAL, "Cannot get a virtual area: %s\n",
109                         strerror(errno));
110                 /* pass errno up the call chain */
111                 rte_errno = errno;
112                 return NULL;
113         } else if (requested_addr != NULL && !addr_is_hint &&
114                         aligned_addr != requested_addr) {
115                 RTE_LOG(ERR, EAL, "Cannot get a virtual area at requested address: %p (got %p)\n",
116                         requested_addr, aligned_addr);
117                 munmap(mapped_addr, map_sz);
118                 rte_errno = EADDRNOTAVAIL;
119                 return NULL;
120         } else if (requested_addr != NULL && addr_is_hint &&
121                         aligned_addr != requested_addr) {
122                 RTE_LOG(WARNING, EAL, "WARNING! Base virtual address hint (%p != %p) not respected!\n",
123                         requested_addr, aligned_addr);
124                 RTE_LOG(WARNING, EAL, "   This may cause issues with mapping memory into secondary processes\n");
125         } else if (next_baseaddr != NULL) {
126                 next_baseaddr = RTE_PTR_ADD(aligned_addr, *size);
127         }
128
129         RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
130                 aligned_addr, *size);
131
132         if (unmap) {
133                 munmap(mapped_addr, map_sz);
134         } else if (!no_align) {
135                 void *map_end, *aligned_end;
136                 size_t before_len, after_len;
137
138                 /* when we reserve space with alignment, we add alignment to
139                  * mapping size. On 32-bit, if 1GB alignment was requested, this
140                  * would waste 1GB of address space, which is a luxury we cannot
141                  * afford. so, if alignment was performed, check if any unneeded
142                  * address space can be unmapped back.
143                  */
144
145                 map_end = RTE_PTR_ADD(mapped_addr, (size_t)map_sz);
146                 aligned_end = RTE_PTR_ADD(aligned_addr, *size);
147
148                 /* unmap space before aligned mmap address */
149                 before_len = RTE_PTR_DIFF(aligned_addr, mapped_addr);
150                 if (before_len > 0)
151                         munmap(mapped_addr, before_len);
152
153                 /* unmap space after aligned end mmap address */
154                 after_len = RTE_PTR_DIFF(map_end, aligned_end);
155                 if (after_len > 0)
156                         munmap(aligned_end, after_len);
157         }
158
159         return aligned_addr;
160 }
161
162 static struct rte_memseg *
163 virt2memseg(const void *addr, const struct rte_memseg_list *msl)
164 {
165         const struct rte_fbarray *arr;
166         void *start, *end;
167         int ms_idx;
168
169         if (msl == NULL)
170                 return NULL;
171
172         /* a memseg list was specified, check if it's the right one */
173         start = msl->base_va;
174         end = RTE_PTR_ADD(start, (size_t)msl->page_sz * msl->memseg_arr.len);
175
176         if (addr < start || addr >= end)
177                 return NULL;
178
179         /* now, calculate index */
180         arr = &msl->memseg_arr;
181         ms_idx = RTE_PTR_DIFF(addr, msl->base_va) / msl->page_sz;
182         return rte_fbarray_get(arr, ms_idx);
183 }
184
185 static struct rte_memseg_list *
186 virt2memseg_list(const void *addr)
187 {
188         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
189         struct rte_memseg_list *msl;
190         int msl_idx;
191
192         for (msl_idx = 0; msl_idx < RTE_MAX_MEMSEG_LISTS; msl_idx++) {
193                 void *start, *end;
194                 msl = &mcfg->memsegs[msl_idx];
195
196                 start = msl->base_va;
197                 end = RTE_PTR_ADD(start,
198                                 (size_t)msl->page_sz * msl->memseg_arr.len);
199                 if (addr >= start && addr < end)
200                         break;
201         }
202         /* if we didn't find our memseg list */
203         if (msl_idx == RTE_MAX_MEMSEG_LISTS)
204                 return NULL;
205         return msl;
206 }
207
208 __rte_experimental struct rte_memseg_list *
209 rte_mem_virt2memseg_list(const void *addr)
210 {
211         return virt2memseg_list(addr);
212 }
213
214 struct virtiova {
215         rte_iova_t iova;
216         void *virt;
217 };
218 static int
219 find_virt(const struct rte_memseg_list *msl __rte_unused,
220                 const struct rte_memseg *ms, void *arg)
221 {
222         struct virtiova *vi = arg;
223         if (vi->iova >= ms->iova && vi->iova < (ms->iova + ms->len)) {
224                 size_t offset = vi->iova - ms->iova;
225                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
226                 /* stop the walk */
227                 return 1;
228         }
229         return 0;
230 }
231 static int
232 find_virt_legacy(const struct rte_memseg_list *msl __rte_unused,
233                 const struct rte_memseg *ms, size_t len, void *arg)
234 {
235         struct virtiova *vi = arg;
236         if (vi->iova >= ms->iova && vi->iova < (ms->iova + len)) {
237                 size_t offset = vi->iova - ms->iova;
238                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
239                 /* stop the walk */
240                 return 1;
241         }
242         return 0;
243 }
244
245 __rte_experimental void *
246 rte_mem_iova2virt(rte_iova_t iova)
247 {
248         struct virtiova vi;
249
250         memset(&vi, 0, sizeof(vi));
251
252         vi.iova = iova;
253         /* for legacy mem, we can get away with scanning VA-contiguous segments,
254          * as we know they are PA-contiguous as well
255          */
256         if (internal_config.legacy_mem)
257                 rte_memseg_contig_walk(find_virt_legacy, &vi);
258         else
259                 rte_memseg_walk(find_virt, &vi);
260
261         return vi.virt;
262 }
263
264 __rte_experimental struct rte_memseg *
265 rte_mem_virt2memseg(const void *addr, const struct rte_memseg_list *msl)
266 {
267         return virt2memseg(addr, msl != NULL ? msl :
268                         rte_mem_virt2memseg_list(addr));
269 }
270
271 static int
272 physmem_size(const struct rte_memseg_list *msl, void *arg)
273 {
274         uint64_t *total_len = arg;
275
276         *total_len += msl->memseg_arr.count * msl->page_sz;
277
278         return 0;
279 }
280
281 /* get the total size of memory */
282 uint64_t
283 rte_eal_get_physmem_size(void)
284 {
285         uint64_t total_len = 0;
286
287         rte_memseg_list_walk(physmem_size, &total_len);
288
289         return total_len;
290 }
291
292 static int
293 dump_memseg(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
294                 void *arg)
295 {
296         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
297         int msl_idx, ms_idx, fd;
298         FILE *f = arg;
299
300         msl_idx = msl - mcfg->memsegs;
301         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
302                 return -1;
303
304         ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
305         if (ms_idx < 0)
306                 return -1;
307
308         fd = eal_memalloc_get_seg_fd(msl_idx, ms_idx);
309         fprintf(f, "Segment %i-%i: IOVA:0x%"PRIx64", len:%zu, "
310                         "virt:%p, socket_id:%"PRId32", "
311                         "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
312                         "nrank:%"PRIx32" fd:%i\n",
313                         msl_idx, ms_idx,
314                         ms->iova,
315                         ms->len,
316                         ms->addr,
317                         ms->socket_id,
318                         ms->hugepage_sz,
319                         ms->nchannel,
320                         ms->nrank,
321                         fd);
322
323         return 0;
324 }
325
326 /*
327  * Defining here because declared in rte_memory.h, but the actual implementation
328  * is in eal_common_memalloc.c, like all other memalloc internals.
329  */
330 int __rte_experimental
331 rte_mem_event_callback_register(const char *name, rte_mem_event_callback_t clb,
332                 void *arg)
333 {
334         /* FreeBSD boots with legacy mem enabled by default */
335         if (internal_config.legacy_mem) {
336                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
337                 rte_errno = ENOTSUP;
338                 return -1;
339         }
340         return eal_memalloc_mem_event_callback_register(name, clb, arg);
341 }
342
343 int __rte_experimental
344 rte_mem_event_callback_unregister(const char *name, void *arg)
345 {
346         /* FreeBSD boots with legacy mem enabled by default */
347         if (internal_config.legacy_mem) {
348                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
349                 rte_errno = ENOTSUP;
350                 return -1;
351         }
352         return eal_memalloc_mem_event_callback_unregister(name, arg);
353 }
354
355 int __rte_experimental
356 rte_mem_alloc_validator_register(const char *name,
357                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
358 {
359         /* FreeBSD boots with legacy mem enabled by default */
360         if (internal_config.legacy_mem) {
361                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
362                 rte_errno = ENOTSUP;
363                 return -1;
364         }
365         return eal_memalloc_mem_alloc_validator_register(name, clb, socket_id,
366                         limit);
367 }
368
369 int __rte_experimental
370 rte_mem_alloc_validator_unregister(const char *name, int socket_id)
371 {
372         /* FreeBSD boots with legacy mem enabled by default */
373         if (internal_config.legacy_mem) {
374                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
375                 rte_errno = ENOTSUP;
376                 return -1;
377         }
378         return eal_memalloc_mem_alloc_validator_unregister(name, socket_id);
379 }
380
381 /* Dump the physical memory layout on console */
382 void
383 rte_dump_physmem_layout(FILE *f)
384 {
385         rte_memseg_walk(dump_memseg, f);
386 }
387
388 /* return the number of memory channels */
389 unsigned rte_memory_get_nchannel(void)
390 {
391         return rte_eal_get_configuration()->mem_config->nchannel;
392 }
393
394 /* return the number of memory rank */
395 unsigned rte_memory_get_nrank(void)
396 {
397         return rte_eal_get_configuration()->mem_config->nrank;
398 }
399
400 static int
401 rte_eal_memdevice_init(void)
402 {
403         struct rte_config *config;
404
405         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
406                 return 0;
407
408         config = rte_eal_get_configuration();
409         config->mem_config->nchannel = internal_config.force_nchannel;
410         config->mem_config->nrank = internal_config.force_nrank;
411
412         return 0;
413 }
414
415 /* Lock page in physical memory and prevent from swapping. */
416 int
417 rte_mem_lock_page(const void *virt)
418 {
419         unsigned long virtual = (unsigned long)virt;
420         int page_size = getpagesize();
421         unsigned long aligned = (virtual & ~(page_size - 1));
422         return mlock((void *)aligned, page_size);
423 }
424
425 int __rte_experimental
426 rte_memseg_contig_walk_thread_unsafe(rte_memseg_contig_walk_t func, void *arg)
427 {
428         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
429         int i, ms_idx, ret = 0;
430
431         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
432                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
433                 const struct rte_memseg *ms;
434                 struct rte_fbarray *arr;
435
436                 if (msl->memseg_arr.count == 0)
437                         continue;
438
439                 arr = &msl->memseg_arr;
440
441                 ms_idx = rte_fbarray_find_next_used(arr, 0);
442                 while (ms_idx >= 0) {
443                         int n_segs;
444                         size_t len;
445
446                         ms = rte_fbarray_get(arr, ms_idx);
447
448                         /* find how many more segments there are, starting with
449                          * this one.
450                          */
451                         n_segs = rte_fbarray_find_contig_used(arr, ms_idx);
452                         len = n_segs * msl->page_sz;
453
454                         ret = func(msl, ms, len, arg);
455                         if (ret)
456                                 return ret;
457                         ms_idx = rte_fbarray_find_next_used(arr,
458                                         ms_idx + n_segs);
459                 }
460         }
461         return 0;
462 }
463
464 int __rte_experimental
465 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
466 {
467         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
468         int ret = 0;
469
470         /* do not allow allocations/frees/init while we iterate */
471         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
472         ret = rte_memseg_contig_walk_thread_unsafe(func, arg);
473         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
474
475         return ret;
476 }
477
478 int __rte_experimental
479 rte_memseg_walk_thread_unsafe(rte_memseg_walk_t func, void *arg)
480 {
481         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
482         int i, ms_idx, ret = 0;
483
484         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
485                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
486                 const struct rte_memseg *ms;
487                 struct rte_fbarray *arr;
488
489                 if (msl->memseg_arr.count == 0)
490                         continue;
491
492                 arr = &msl->memseg_arr;
493
494                 ms_idx = rte_fbarray_find_next_used(arr, 0);
495                 while (ms_idx >= 0) {
496                         ms = rte_fbarray_get(arr, ms_idx);
497                         ret = func(msl, ms, arg);
498                         if (ret)
499                                 return ret;
500                         ms_idx = rte_fbarray_find_next_used(arr, ms_idx + 1);
501                 }
502         }
503         return 0;
504 }
505
506 int __rte_experimental
507 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
508 {
509         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
510         int ret = 0;
511
512         /* do not allow allocations/frees/init while we iterate */
513         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
514         ret = rte_memseg_walk_thread_unsafe(func, arg);
515         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
516
517         return ret;
518 }
519
520 int __rte_experimental
521 rte_memseg_list_walk_thread_unsafe(rte_memseg_list_walk_t func, void *arg)
522 {
523         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
524         int i, ret = 0;
525
526         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
527                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
528
529                 if (msl->base_va == NULL)
530                         continue;
531
532                 ret = func(msl, arg);
533                 if (ret)
534                         return ret;
535         }
536         return 0;
537 }
538
539 int __rte_experimental
540 rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)
541 {
542         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
543         int ret = 0;
544
545         /* do not allow allocations/frees/init while we iterate */
546         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
547         ret = rte_memseg_list_walk_thread_unsafe(func, arg);
548         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
549
550         return ret;
551 }
552
553 int __rte_experimental
554 rte_memseg_get_fd_thread_unsafe(const struct rte_memseg *ms)
555 {
556         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
557         struct rte_memseg_list *msl;
558         struct rte_fbarray *arr;
559         int msl_idx, seg_idx, ret;
560
561         if (ms == NULL) {
562                 rte_errno = EINVAL;
563                 return -1;
564         }
565
566         msl = rte_mem_virt2memseg_list(ms->addr);
567         if (msl == NULL) {
568                 rte_errno = EINVAL;
569                 return -1;
570         }
571         arr = &msl->memseg_arr;
572
573         msl_idx = msl - mcfg->memsegs;
574         seg_idx = rte_fbarray_find_idx(arr, ms);
575
576         if (!rte_fbarray_is_used(arr, seg_idx)) {
577                 rte_errno = ENOENT;
578                 return -1;
579         }
580
581         ret = eal_memalloc_get_seg_fd(msl_idx, seg_idx);
582         if (ret < 0) {
583                 rte_errno = -ret;
584                 ret = -1;
585         }
586         return ret;
587 }
588
589 int __rte_experimental
590 rte_memseg_get_fd(const struct rte_memseg *ms)
591 {
592         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
593         int ret;
594
595         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
596         ret = rte_memseg_get_fd_thread_unsafe(ms);
597         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
598
599         return ret;
600 }
601
602 int __rte_experimental
603 rte_memseg_get_fd_offset_thread_unsafe(const struct rte_memseg *ms,
604                 size_t *offset)
605 {
606         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
607         struct rte_memseg_list *msl;
608         struct rte_fbarray *arr;
609         int msl_idx, seg_idx, ret;
610
611         if (ms == NULL || offset == NULL) {
612                 rte_errno = EINVAL;
613                 return -1;
614         }
615
616         msl = rte_mem_virt2memseg_list(ms->addr);
617         if (msl == NULL) {
618                 rte_errno = EINVAL;
619                 return -1;
620         }
621         arr = &msl->memseg_arr;
622
623         msl_idx = msl - mcfg->memsegs;
624         seg_idx = rte_fbarray_find_idx(arr, ms);
625
626         if (!rte_fbarray_is_used(arr, seg_idx)) {
627                 rte_errno = ENOENT;
628                 return -1;
629         }
630
631         ret = eal_memalloc_get_seg_fd_offset(msl_idx, seg_idx, offset);
632         if (ret < 0) {
633                 rte_errno = -ret;
634                 ret = -1;
635         }
636         return ret;
637 }
638
639 int __rte_experimental
640 rte_memseg_get_fd_offset(const struct rte_memseg *ms, size_t *offset)
641 {
642         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
643         int ret;
644
645         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
646         ret = rte_memseg_get_fd_offset_thread_unsafe(ms, offset);
647         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
648
649         return ret;
650 }
651
652 /* init memory subsystem */
653 int
654 rte_eal_memory_init(void)
655 {
656         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
657         int retval;
658         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
659
660         if (!mcfg)
661                 return -1;
662
663         /* lock mem hotplug here, to prevent races while we init */
664         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
665
666         if (rte_eal_memseg_init() < 0)
667                 goto fail;
668
669         if (eal_memalloc_init() < 0)
670                 goto fail;
671
672         retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
673                         rte_eal_hugepage_init() :
674                         rte_eal_hugepage_attach();
675         if (retval < 0)
676                 goto fail;
677
678         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
679                 goto fail;
680
681         return 0;
682 fail:
683         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
684         return -1;
685 }