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