mem: allow memseg lists to be marked as external
[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, msl->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, msl->len);
198                 if (addr >= start && addr < end)
199                         break;
200         }
201         /* if we didn't find our memseg list */
202         if (msl_idx == RTE_MAX_MEMSEG_LISTS)
203                 return NULL;
204         return msl;
205 }
206
207 __rte_experimental struct rte_memseg_list *
208 rte_mem_virt2memseg_list(const void *addr)
209 {
210         return virt2memseg_list(addr);
211 }
212
213 struct virtiova {
214         rte_iova_t iova;
215         void *virt;
216 };
217 static int
218 find_virt(const struct rte_memseg_list *msl __rte_unused,
219                 const struct rte_memseg *ms, void *arg)
220 {
221         struct virtiova *vi = arg;
222         if (vi->iova >= ms->iova && vi->iova < (ms->iova + ms->len)) {
223                 size_t offset = vi->iova - ms->iova;
224                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
225                 /* stop the walk */
226                 return 1;
227         }
228         return 0;
229 }
230 static int
231 find_virt_legacy(const struct rte_memseg_list *msl __rte_unused,
232                 const struct rte_memseg *ms, size_t len, void *arg)
233 {
234         struct virtiova *vi = arg;
235         if (vi->iova >= ms->iova && vi->iova < (ms->iova + len)) {
236                 size_t offset = vi->iova - ms->iova;
237                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
238                 /* stop the walk */
239                 return 1;
240         }
241         return 0;
242 }
243
244 __rte_experimental void *
245 rte_mem_iova2virt(rte_iova_t iova)
246 {
247         struct virtiova vi;
248
249         memset(&vi, 0, sizeof(vi));
250
251         vi.iova = iova;
252         /* for legacy mem, we can get away with scanning VA-contiguous segments,
253          * as we know they are PA-contiguous as well
254          */
255         if (internal_config.legacy_mem)
256                 rte_memseg_contig_walk(find_virt_legacy, &vi);
257         else
258                 rte_memseg_walk(find_virt, &vi);
259
260         return vi.virt;
261 }
262
263 __rte_experimental struct rte_memseg *
264 rte_mem_virt2memseg(const void *addr, const struct rte_memseg_list *msl)
265 {
266         return virt2memseg(addr, msl != NULL ? msl :
267                         rte_mem_virt2memseg_list(addr));
268 }
269
270 static int
271 physmem_size(const struct rte_memseg_list *msl, void *arg)
272 {
273         uint64_t *total_len = arg;
274
275         if (msl->external)
276                 return 0;
277
278         *total_len += msl->memseg_arr.count * msl->page_sz;
279
280         return 0;
281 }
282
283 /* get the total size of memory */
284 uint64_t
285 rte_eal_get_physmem_size(void)
286 {
287         uint64_t total_len = 0;
288
289         rte_memseg_list_walk(physmem_size, &total_len);
290
291         return total_len;
292 }
293
294 static int
295 dump_memseg(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
296                 void *arg)
297 {
298         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
299         int msl_idx, ms_idx, fd;
300         FILE *f = arg;
301
302         msl_idx = msl - mcfg->memsegs;
303         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
304                 return -1;
305
306         ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
307         if (ms_idx < 0)
308                 return -1;
309
310         fd = eal_memalloc_get_seg_fd(msl_idx, ms_idx);
311         fprintf(f, "Segment %i-%i: IOVA:0x%"PRIx64", len:%zu, "
312                         "virt:%p, socket_id:%"PRId32", "
313                         "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
314                         "nrank:%"PRIx32" fd:%i\n",
315                         msl_idx, ms_idx,
316                         ms->iova,
317                         ms->len,
318                         ms->addr,
319                         ms->socket_id,
320                         ms->hugepage_sz,
321                         ms->nchannel,
322                         ms->nrank,
323                         fd);
324
325         return 0;
326 }
327
328 /*
329  * Defining here because declared in rte_memory.h, but the actual implementation
330  * is in eal_common_memalloc.c, like all other memalloc internals.
331  */
332 int __rte_experimental
333 rte_mem_event_callback_register(const char *name, rte_mem_event_callback_t clb,
334                 void *arg)
335 {
336         /* FreeBSD boots with legacy mem enabled by default */
337         if (internal_config.legacy_mem) {
338                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
339                 rte_errno = ENOTSUP;
340                 return -1;
341         }
342         return eal_memalloc_mem_event_callback_register(name, clb, arg);
343 }
344
345 int __rte_experimental
346 rte_mem_event_callback_unregister(const char *name, void *arg)
347 {
348         /* FreeBSD boots with legacy mem enabled by default */
349         if (internal_config.legacy_mem) {
350                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
351                 rte_errno = ENOTSUP;
352                 return -1;
353         }
354         return eal_memalloc_mem_event_callback_unregister(name, arg);
355 }
356
357 int __rte_experimental
358 rte_mem_alloc_validator_register(const char *name,
359                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
360 {
361         /* FreeBSD boots with legacy mem enabled by default */
362         if (internal_config.legacy_mem) {
363                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
364                 rte_errno = ENOTSUP;
365                 return -1;
366         }
367         return eal_memalloc_mem_alloc_validator_register(name, clb, socket_id,
368                         limit);
369 }
370
371 int __rte_experimental
372 rte_mem_alloc_validator_unregister(const char *name, int socket_id)
373 {
374         /* FreeBSD boots with legacy mem enabled by default */
375         if (internal_config.legacy_mem) {
376                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
377                 rte_errno = ENOTSUP;
378                 return -1;
379         }
380         return eal_memalloc_mem_alloc_validator_unregister(name, socket_id);
381 }
382
383 /* Dump the physical memory layout on console */
384 void
385 rte_dump_physmem_layout(FILE *f)
386 {
387         rte_memseg_walk(dump_memseg, f);
388 }
389
390 /* return the number of memory channels */
391 unsigned rte_memory_get_nchannel(void)
392 {
393         return rte_eal_get_configuration()->mem_config->nchannel;
394 }
395
396 /* return the number of memory rank */
397 unsigned rte_memory_get_nrank(void)
398 {
399         return rte_eal_get_configuration()->mem_config->nrank;
400 }
401
402 static int
403 rte_eal_memdevice_init(void)
404 {
405         struct rte_config *config;
406
407         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
408                 return 0;
409
410         config = rte_eal_get_configuration();
411         config->mem_config->nchannel = internal_config.force_nchannel;
412         config->mem_config->nrank = internal_config.force_nrank;
413
414         return 0;
415 }
416
417 /* Lock page in physical memory and prevent from swapping. */
418 int
419 rte_mem_lock_page(const void *virt)
420 {
421         unsigned long virtual = (unsigned long)virt;
422         int page_size = getpagesize();
423         unsigned long aligned = (virtual & ~(page_size - 1));
424         return mlock((void *)aligned, page_size);
425 }
426
427 int __rte_experimental
428 rte_memseg_contig_walk_thread_unsafe(rte_memseg_contig_walk_t func, void *arg)
429 {
430         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
431         int i, ms_idx, ret = 0;
432
433         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
434                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
435                 const struct rte_memseg *ms;
436                 struct rte_fbarray *arr;
437
438                 if (msl->memseg_arr.count == 0)
439                         continue;
440
441                 arr = &msl->memseg_arr;
442
443                 ms_idx = rte_fbarray_find_next_used(arr, 0);
444                 while (ms_idx >= 0) {
445                         int n_segs;
446                         size_t len;
447
448                         ms = rte_fbarray_get(arr, ms_idx);
449
450                         /* find how many more segments there are, starting with
451                          * this one.
452                          */
453                         n_segs = rte_fbarray_find_contig_used(arr, ms_idx);
454                         len = n_segs * msl->page_sz;
455
456                         ret = func(msl, ms, len, arg);
457                         if (ret)
458                                 return ret;
459                         ms_idx = rte_fbarray_find_next_used(arr,
460                                         ms_idx + n_segs);
461                 }
462         }
463         return 0;
464 }
465
466 int __rte_experimental
467 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
468 {
469         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
470         int ret = 0;
471
472         /* do not allow allocations/frees/init while we iterate */
473         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
474         ret = rte_memseg_contig_walk_thread_unsafe(func, arg);
475         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
476
477         return ret;
478 }
479
480 int __rte_experimental
481 rte_memseg_walk_thread_unsafe(rte_memseg_walk_t func, void *arg)
482 {
483         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
484         int i, ms_idx, ret = 0;
485
486         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
487                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
488                 const struct rte_memseg *ms;
489                 struct rte_fbarray *arr;
490
491                 if (msl->memseg_arr.count == 0)
492                         continue;
493
494                 arr = &msl->memseg_arr;
495
496                 ms_idx = rte_fbarray_find_next_used(arr, 0);
497                 while (ms_idx >= 0) {
498                         ms = rte_fbarray_get(arr, ms_idx);
499                         ret = func(msl, ms, arg);
500                         if (ret)
501                                 return ret;
502                         ms_idx = rte_fbarray_find_next_used(arr, ms_idx + 1);
503                 }
504         }
505         return 0;
506 }
507
508 int __rte_experimental
509 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
510 {
511         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
512         int ret = 0;
513
514         /* do not allow allocations/frees/init while we iterate */
515         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
516         ret = rte_memseg_walk_thread_unsafe(func, arg);
517         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
518
519         return ret;
520 }
521
522 int __rte_experimental
523 rte_memseg_list_walk_thread_unsafe(rte_memseg_list_walk_t func, void *arg)
524 {
525         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
526         int i, ret = 0;
527
528         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
529                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
530
531                 if (msl->base_va == NULL)
532                         continue;
533
534                 ret = func(msl, arg);
535                 if (ret)
536                         return ret;
537         }
538         return 0;
539 }
540
541 int __rte_experimental
542 rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)
543 {
544         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
545         int ret = 0;
546
547         /* do not allow allocations/frees/init while we iterate */
548         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
549         ret = rte_memseg_list_walk_thread_unsafe(func, arg);
550         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
551
552         return ret;
553 }
554
555 int __rte_experimental
556 rte_memseg_get_fd_thread_unsafe(const struct rte_memseg *ms)
557 {
558         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
559         struct rte_memseg_list *msl;
560         struct rte_fbarray *arr;
561         int msl_idx, seg_idx, ret;
562
563         if (ms == NULL) {
564                 rte_errno = EINVAL;
565                 return -1;
566         }
567
568         msl = rte_mem_virt2memseg_list(ms->addr);
569         if (msl == NULL) {
570                 rte_errno = EINVAL;
571                 return -1;
572         }
573         arr = &msl->memseg_arr;
574
575         msl_idx = msl - mcfg->memsegs;
576         seg_idx = rte_fbarray_find_idx(arr, ms);
577
578         if (!rte_fbarray_is_used(arr, seg_idx)) {
579                 rte_errno = ENOENT;
580                 return -1;
581         }
582
583         ret = eal_memalloc_get_seg_fd(msl_idx, seg_idx);
584         if (ret < 0) {
585                 rte_errno = -ret;
586                 ret = -1;
587         }
588         return ret;
589 }
590
591 int __rte_experimental
592 rte_memseg_get_fd(const struct rte_memseg *ms)
593 {
594         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
595         int ret;
596
597         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
598         ret = rte_memseg_get_fd_thread_unsafe(ms);
599         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
600
601         return ret;
602 }
603
604 int __rte_experimental
605 rte_memseg_get_fd_offset_thread_unsafe(const struct rte_memseg *ms,
606                 size_t *offset)
607 {
608         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
609         struct rte_memseg_list *msl;
610         struct rte_fbarray *arr;
611         int msl_idx, seg_idx, ret;
612
613         if (ms == NULL || offset == NULL) {
614                 rte_errno = EINVAL;
615                 return -1;
616         }
617
618         msl = rte_mem_virt2memseg_list(ms->addr);
619         if (msl == NULL) {
620                 rte_errno = EINVAL;
621                 return -1;
622         }
623         arr = &msl->memseg_arr;
624
625         msl_idx = msl - mcfg->memsegs;
626         seg_idx = rte_fbarray_find_idx(arr, ms);
627
628         if (!rte_fbarray_is_used(arr, seg_idx)) {
629                 rte_errno = ENOENT;
630                 return -1;
631         }
632
633         ret = eal_memalloc_get_seg_fd_offset(msl_idx, seg_idx, offset);
634         if (ret < 0) {
635                 rte_errno = -ret;
636                 ret = -1;
637         }
638         return ret;
639 }
640
641 int __rte_experimental
642 rte_memseg_get_fd_offset(const struct rte_memseg *ms, size_t *offset)
643 {
644         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
645         int ret;
646
647         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
648         ret = rte_memseg_get_fd_offset_thread_unsafe(ms, offset);
649         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
650
651         return ret;
652 }
653
654 /* init memory subsystem */
655 int
656 rte_eal_memory_init(void)
657 {
658         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
659         int retval;
660         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
661
662         if (!mcfg)
663                 return -1;
664
665         /* lock mem hotplug here, to prevent races while we init */
666         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
667
668         if (rte_eal_memseg_init() < 0)
669                 goto fail;
670
671         if (eal_memalloc_init() < 0)
672                 goto fail;
673
674         retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
675                         rte_eal_hugepage_init() :
676                         rte_eal_hugepage_attach();
677         if (retval < 0)
678                 goto fail;
679
680         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
681                 goto fail;
682
683         return 0;
684 fail:
685         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
686         return -1;
687 }