1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2012. ALL RIGHTS RESERVED.
3 * Copyright (c) UT-Battelle, LLC. 2014-2019. ALL RIGHTS RESERVED.
4 * Copyright (C) ARM Ltd. 2016-2017. ALL RIGHTS RESERVED.
5 *
6 * See file LICENSE for terms.
7 */
8
9 #ifdef HAVE_CONFIG_H
10 # include "config.h"
11 #endif
12
13
14 #include <ucs/algorithm/crc.h>
15 #include <ucs/sys/checker.h>
16 #include <ucs/sys/string.h>
17 #include <ucs/sys/sys.h>
18 #include <ucs/debug/log.h>
19 #include <ucs/time/time.h>
20 #include <ucs/type/init_once.h>
21 #include <ucm/util/sys.h>
22
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/ioctl.h>
26 #include <sys/shm.h>
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 #include <net/if.h>
30 #include <dirent.h>
31 #include <sched.h>
32 #include <ctype.h>
33 #ifdef HAVE_SYS_THR_H
34 #include <sys/thr.h>
35 #endif
36
37 #if HAVE_SYS_CAPABILITY_H
38 # include <sys/capability.h>
39 #endif
40
41 /* Default huge page size is 2 MBytes */
42 #define UCS_DEFAULT_MEM_FREE 640000
43 #define UCS_PROCESS_SMAPS_FILE "/proc/self/smaps"
44 #define UCS_PROCESS_NS_DIR "/proc/self/ns"
45 #define UCS_PROCESS_BOOTID_FILE "/proc/sys/kernel/random/boot_id"
46 #define UCS_PROCESS_BOOTID_FMT "%x-%4hx-%4hx-%4hx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx"
47 #define UCS_PROCESS_NS_FIRST 0xF0000000U
48 #define UCS_PROCESS_NS_NET_DFLT 0xF0000080U
49
50
51 struct {
52 const char *name;
53 ucs_sys_ns_t dflt;
54 } static ucs_sys_namespace_info[] = {
55 [UCS_SYS_NS_TYPE_IPC] = {.name = "ipc", .dflt = UCS_PROCESS_NS_FIRST - 1},
56 [UCS_SYS_NS_TYPE_MNT] = {.name = "mnt", .dflt = UCS_PROCESS_NS_FIRST - 0},
57 [UCS_SYS_NS_TYPE_NET] = {.name = "net", .dflt = UCS_PROCESS_NS_NET_DFLT},
58 [UCS_SYS_NS_TYPE_PID] = {.name = "pid", .dflt = UCS_PROCESS_NS_FIRST - 4},
59 [UCS_SYS_NS_TYPE_USER] = {.name = "user", .dflt = UCS_PROCESS_NS_FIRST - 3},
60 [UCS_SYS_NS_TYPE_UTS] = {.name = "uts", .dflt = UCS_PROCESS_NS_FIRST - 2}
61 };
62
63 typedef struct {
64 void *ctx;
65 ucs_sys_enum_threads_cb_t cb;
66 } ucs_sys_enum_threads_t;
67
68
69 static const char *ucs_pagemap_file = "/proc/self/pagemap";
70
71
ucs_get_tmpdir()72 const char *ucs_get_tmpdir()
73 {
74 char *env_tmpdir;
75
76 env_tmpdir = getenv("TMPDIR");
77 if (env_tmpdir) {
78 return env_tmpdir;
79 } else {
80 return "/tmp/";
81 }
82 }
83
ucs_get_host_name()84 const char *ucs_get_host_name()
85 {
86 static char hostname[HOST_NAME_MAX] = {0};
87
88 if (*hostname == 0) {
89 gethostname(hostname, sizeof(hostname));
90 strtok(hostname, ".");
91 }
92 return hostname;
93 }
94
ucs_get_user_name()95 const char *ucs_get_user_name()
96 {
97 static char username[256] = {0};
98
99 if (*username == 0) {
100 getlogin_r(username, sizeof(username));
101 }
102 return username;
103 }
104
ucs_expand_path(const char * path,char * fullpath,size_t max)105 void ucs_expand_path(const char *path, char *fullpath, size_t max)
106 {
107 char cwd[1024] = {0};
108
109 if (path[0] == '/') {
110 strncpy(fullpath, path, max);
111 } else if (getcwd(cwd, sizeof(cwd) - 1) != NULL) {
112 snprintf(fullpath, max, "%s/%s", cwd, path);
113 } else {
114 ucs_warn("failed to expand path '%s' (%m), using original path", path);
115 strncpy(fullpath, path, max);
116 }
117 }
118
ucs_get_exe()119 const char *ucs_get_exe()
120 {
121 static char exe[1024];
122 int ret;
123
124 ret = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
125 if (ret < 0) {
126 exe[0] = '\0';
127 } else {
128 exe[ret] = '\0';
129 }
130
131 return exe;
132 }
133
ucs_file_checksum(const char * filename)134 uint32_t ucs_file_checksum(const char *filename)
135 {
136 char buffer[1024];
137 ssize_t nread;
138 int fd;
139 uint32_t crc;
140
141 fd = open(filename, O_RDONLY);
142 if (fd < 0) {
143 return 0;
144 }
145
146 crc = 0;
147 do {
148 nread = read(fd, buffer, sizeof(buffer));
149 if (nread > 0) {
150 crc = ucs_crc32(crc, buffer, nread);
151 }
152 } while (nread == sizeof(buffer));
153 close(fd);
154
155 return crc;
156 }
157
ucs_get_mac_address()158 static uint64_t ucs_get_mac_address()
159 {
160 static uint64_t mac_address = 0;
161 struct ifreq ifr, *it, *end;
162 struct ifconf ifc;
163 char buf[1024];
164 int sock;
165
166 if (mac_address == 0) {
167 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
168 if (sock == -1) {
169 ucs_error("failed to create socket: %m");
170 return 0;
171 }
172
173 ifc.ifc_len = sizeof(buf);
174 ifc.ifc_buf = buf;
175 if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
176 ucs_error("ioctl(SIOCGIFCONF) failed: %m");
177 close(sock);
178 return 0;
179 }
180
181 it = ifc.ifc_req;
182 end = it + (ifc.ifc_len / sizeof *it);
183 for (it = ifc.ifc_req; it != end; ++it) {
184 strcpy(ifr.ifr_name, it->ifr_name);
185 if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
186 ucs_error("ioctl(SIOCGIFFLAGS) failed: %m");
187 close(sock);
188 return 0;
189 }
190
191 if (!(ifr.ifr_flags & IFF_LOOPBACK)) {
192 if (ioctl(sock, SIOCGIFHWADDR, &ifr) != 0) {
193 ucs_error("ioctl(SIOCGIFHWADDR) failed: %m");
194 close(sock);
195 return 0;
196 }
197
198 memcpy(&mac_address, ifr.ifr_hwaddr.sa_data, 6);
199 break;
200 }
201 }
202
203 close(sock);
204 ucs_trace("MAC address is 0x%012"PRIX64, mac_address);
205 }
206
207 return mac_address;
208 }
209
__sumup_host_name(unsigned prime_index)210 static uint64_t __sumup_host_name(unsigned prime_index)
211 {
212 uint64_t sum, n;
213 const char *p;
214 unsigned i;
215
216 sum = 0;
217 i = prime_index;
218 p = ucs_get_host_name();
219 while (*p != '\0') {
220 n = 0;
221 memcpy(&n, p, strnlen(p, sizeof(n)));
222 sum += ucs_get_prime(i) * n;
223 ++i;
224 p += ucs_min(sizeof(n), strlen(p));
225 }
226 return sum;
227 }
228
ucs_machine_guid()229 uint64_t ucs_machine_guid()
230 {
231 return ucs_get_prime(0) * ucs_get_mac_address() +
232 __sumup_host_name(1);
233 }
234
235 /*
236 * If a certain system constant (name) is undefined on the underlying system the
237 * sysconf routine returns -1. ucs_sysconf return the negative value
238 * a user and the user is responsible to define default value or abort.
239 *
240 * If an error occurs sysconf modified errno and ucs_sysconf aborts.
241 *
242 * Otherwise, a non-negative values is returned.
243 */
ucs_sysconf(int name)244 static long ucs_sysconf(int name)
245 {
246 long rc;
247 errno = 0;
248
249 rc = sysconf(name);
250 ucs_assert_always(errno == 0);
251
252 return rc;
253 }
254
ucs_get_first_cpu()255 int ucs_get_first_cpu()
256 {
257 int first_cpu, total_cpus, ret;
258 ucs_sys_cpuset_t mask;
259
260 ret = ucs_sysconf(_SC_NPROCESSORS_CONF);
261 if (ret < 0) {
262 ucs_error("failed to get local cpu count: %m");
263 return ret;
264 }
265 total_cpus = ret;
266
267 CPU_ZERO(&mask);
268 ret = ucs_sys_getaffinity(&mask);
269 if (ret < 0) {
270 ucs_error("failed to get process affinity: %m");
271 return ret;
272 }
273
274 for (first_cpu = 0; first_cpu < total_cpus; ++first_cpu) {
275 if (CPU_ISSET(first_cpu, &mask)) {
276 return first_cpu;
277 }
278 }
279
280 return total_cpus;
281 }
282
ucs_generate_uuid(uint64_t seed)283 uint64_t ucs_generate_uuid(uint64_t seed)
284 {
285 struct timeval tv;
286
287 gettimeofday(&tv, NULL);
288 return seed +
289 ucs_get_prime(0) * ucs_get_tid() +
290 ucs_get_prime(1) * ucs_get_time() +
291 ucs_get_prime(2) * ucs_get_mac_address() +
292 ucs_get_prime(3) * tv.tv_sec +
293 ucs_get_prime(4) * tv.tv_usec +
294 __sumup_host_name(5);
295 }
296
297 ucs_status_t
ucs_open_output_stream(const char * config_str,ucs_log_level_t err_log_level,FILE ** p_fstream,int * p_need_close,const char ** p_next_token,char ** p_filename)298 ucs_open_output_stream(const char *config_str, ucs_log_level_t err_log_level,
299 FILE **p_fstream, int *p_need_close,
300 const char **p_next_token, char **p_filename)
301 {
302 FILE *output_stream;
303 char filename[256];
304 char *template;
305 const char *p;
306 size_t len;
307
308 *p_next_token = config_str;
309 if (p_filename != NULL) {
310 *p_filename = NULL;
311 }
312
313 len = strcspn(config_str, ":");
314 if (!strncmp(config_str, "stdout", len)) {
315 *p_fstream = stdout;
316 *p_need_close = 0;
317 *p_next_token = config_str + len;
318 } else if (!strncmp(config_str, "stderr", len)) {
319 *p_fstream = stderr;
320 *p_need_close = 0;
321 *p_next_token = config_str + len;
322 } else {
323 if (!strncmp(config_str, "file:", 5)) {
324 p = config_str + 5;
325 } else {
326 p = config_str;
327 }
328
329 len = strcspn(p, ":");
330 template = strndup(p, len);
331 ucs_fill_filename_template(template, filename, sizeof(filename));
332 free(template);
333
334 output_stream = fopen(filename, "w");
335 if (output_stream == NULL) {
336 ucs_log(err_log_level, "failed to open '%s' for writing: %m",
337 filename);
338 return UCS_ERR_IO_ERROR;
339 }
340
341 if (p_filename != NULL) {
342 *p_filename = ucs_strdup(filename, "filename");
343 if (*p_filename == NULL) {
344 ucs_log(err_log_level, "failed to allocate filename for '%s'",
345 filename);
346 fclose(output_stream);
347 return UCS_ERR_NO_MEMORY;
348 }
349 }
350
351 *p_fstream = output_stream;
352 *p_need_close = 1;
353 *p_next_token = p + len;
354 }
355
356 return UCS_OK;
357 }
358
ucs_read_file_vararg(char * buffer,size_t max,int silent,const char * filename_fmt,va_list ap)359 static ssize_t ucs_read_file_vararg(char *buffer, size_t max, int silent,
360 const char *filename_fmt, va_list ap)
361 {
362 char filename[MAXPATHLEN];
363 ssize_t read_bytes;
364 int fd;
365
366 vsnprintf(filename, MAXPATHLEN, filename_fmt, ap);
367
368 fd = open(filename, O_RDONLY);
369 if (fd < 0) {
370 if (!silent) {
371 ucs_error("failed to open %s: %m", filename);
372 }
373 read_bytes = -1;
374 goto out;
375 }
376
377 read_bytes = read(fd, buffer, max - 1);
378 if (read_bytes < 0) {
379 if (!silent) {
380 ucs_error("failed to read from %s: %m", filename);
381 }
382 goto out_close;
383 }
384
385 if (read_bytes < max) {
386 buffer[read_bytes] = '\0';
387 }
388
389 out_close:
390 close(fd);
391 out:
392 return read_bytes;
393 }
394
ucs_read_file(char * buffer,size_t max,int silent,const char * filename_fmt,...)395 ssize_t ucs_read_file(char *buffer, size_t max, int silent,
396 const char *filename_fmt, ...)
397 {
398 ssize_t read_bytes;
399 va_list ap;
400
401 va_start(ap, filename_fmt);
402 read_bytes = ucs_read_file_vararg(buffer, max, silent, filename_fmt, ap);
403 va_end(ap);
404
405 return read_bytes;
406 }
407
ucs_read_file_number(long * value,int silent,const char * filename_fmt,...)408 ucs_status_t ucs_read_file_number(long *value, int silent,
409 const char *filename_fmt, ...)
410 {
411 char buffer[64], *tail;
412 ssize_t read_bytes;
413 va_list ap;
414 long n;
415
416 va_start(ap, filename_fmt);
417 read_bytes = ucs_read_file_vararg(buffer, sizeof(buffer) - 1, silent,
418 filename_fmt, ap);
419 va_end(ap);
420
421 if (read_bytes < 0) {
422 /* read error */
423 return UCS_ERR_IO_ERROR;
424 }
425
426 n = strtol(buffer, &tail, 0);
427 if ((*tail != '\0') && !isspace(*tail)) {
428 /* parse error */
429 return UCS_ERR_INVALID_PARAM;
430 }
431
432 *value = n;
433 return UCS_OK;
434 }
435
ucs_read_file_str(char * buffer,size_t max,int silent,const char * filename_fmt,...)436 ssize_t ucs_read_file_str(char *buffer, size_t max, int silent,
437 const char *filename_fmt, ...)
438 {
439 size_t max_read = ucs_max(max, 1) - 1;
440 ssize_t read_bytes;
441 va_list ap;
442
443 va_start(ap, filename_fmt);
444 read_bytes = ucs_read_file_vararg(buffer, max_read, silent, filename_fmt, ap);
445 va_end(ap);
446
447 if ((read_bytes >= 0) && (max > 0)) {
448 buffer[read_bytes] = '\0';
449 }
450
451 return read_bytes;
452 }
453
ucs_get_page_size()454 size_t ucs_get_page_size()
455 {
456 static long page_size = 0;
457
458 if (page_size == 0) {
459 page_size = ucs_sysconf(_SC_PAGESIZE);
460 if (page_size < 0) {
461 page_size = 4096;
462 ucs_debug("_SC_PAGESIZE is undefined, setting default value to %ld",
463 page_size);
464 }
465 }
466 return page_size;
467 }
468
ucs_get_mem_page_size(void * address,size_t size,size_t * min_page_size_p,size_t * max_page_size_p)469 void ucs_get_mem_page_size(void *address, size_t size, size_t *min_page_size_p,
470 size_t *max_page_size_p)
471 {
472 int found = 0;
473 unsigned long start, end;
474 unsigned long page_size_kb;
475 size_t page_size;
476 char buf[1024];
477 FILE *file;
478 int n;
479
480 file = fopen(UCS_PROCESS_SMAPS_FILE, "r");
481 if (!file) {
482 goto out;
483 }
484
485 while (fgets(buf, sizeof(buf), file) != NULL) {
486 n = sscanf(buf, "%lx-%lx", &start, &end);
487 if (n != 2) {
488 continue;
489 }
490
491 if (start > (uintptr_t)address + size) {
492 /* the scanned range is after memory range of interest - stop */
493 break;
494 }
495 if (end <= (uintptr_t)address) {
496 /* the scanned range is still before the memory range of interest */
497 continue;
498 }
499
500 while (fgets(buf, sizeof(buf), file) != NULL) {
501 n = sscanf(buf, "KernelPageSize: %lu kB", &page_size_kb);
502 if (n < 1) {
503 continue;
504 }
505
506 page_size = page_size_kb * UCS_KBYTE;
507 if (found) {
508 *min_page_size_p = ucs_min(*min_page_size_p, page_size);
509 *max_page_size_p = ucs_max(*max_page_size_p, page_size);
510 } else {
511 found = 1;
512 *min_page_size_p = page_size;
513 *max_page_size_p = page_size;
514 }
515 break;
516 }
517 }
518
519 fclose(file);
520
521 out:
522 if (!found) {
523 *min_page_size_p = *max_page_size_p = ucs_get_page_size();
524 }
525 }
526
ucs_get_meminfo_entry(const char * pattern)527 static ssize_t ucs_get_meminfo_entry(const char* pattern)
528 {
529 char buf[256];
530 char final_pattern[80];
531 int val = 0;
532 ssize_t val_b = -1;
533 FILE *f;
534
535 f = fopen("/proc/meminfo", "r");
536 if (f != NULL) {
537 snprintf(final_pattern, sizeof(final_pattern), "%s: %s", pattern,
538 "%d kB");
539 while (fgets(buf, sizeof(buf), f)) {
540 if (sscanf(buf, final_pattern, &val) == 1) {
541 val_b = val * 1024ull;
542 break;
543 }
544 }
545 fclose(f);
546 }
547
548 return val_b;
549 }
550
ucs_get_memfree_size()551 size_t ucs_get_memfree_size()
552 {
553 ssize_t mem_free;
554
555 mem_free = ucs_get_meminfo_entry("MemFree");
556 if (mem_free == -1) {
557 mem_free = UCS_DEFAULT_MEM_FREE;
558 ucs_info("cannot determine free mem size, using default: %zu",
559 mem_free);
560 }
561
562 return mem_free;
563 }
564
ucs_get_huge_page_size()565 ssize_t ucs_get_huge_page_size()
566 {
567 static ssize_t huge_page_size = 0;
568
569 /* Cache the huge page size value */
570 if (huge_page_size == 0) {
571 huge_page_size = ucs_get_meminfo_entry("Hugepagesize");
572 if (huge_page_size == -1) {
573 ucs_debug("huge pages are not supported on the system");
574 } else {
575 ucs_trace("detected huge page size: %zu", huge_page_size);
576 }
577 }
578
579 return huge_page_size;
580 }
581
ucs_get_phys_mem_size()582 size_t ucs_get_phys_mem_size()
583 {
584 static size_t phys_mem_size = 0;
585 long phys_pages;
586
587 if (phys_mem_size == 0) {
588 phys_pages = ucs_sysconf(_SC_PHYS_PAGES);
589 if (phys_pages < 0) {
590 ucs_debug("_SC_PHYS_PAGES is undefined, setting default value to %ld",
591 SIZE_MAX);
592 phys_mem_size = SIZE_MAX;
593 } else {
594 phys_mem_size = phys_pages * ucs_get_page_size();
595 }
596 }
597 return phys_mem_size;
598 }
599
600 #define UCS_SYS_THP_ENABLED_FILE "/sys/kernel/mm/transparent_hugepage/enabled"
ucs_is_thp_enabled()601 int ucs_is_thp_enabled()
602 {
603 char buf[256];
604 int rc;
605
606 rc = ucs_read_file(buf, sizeof(buf) - 1, 1, UCS_SYS_THP_ENABLED_FILE);
607 if (rc < 0) {
608 ucs_debug("failed to read %s:%m", UCS_SYS_THP_ENABLED_FILE);
609 return 0;
610 }
611
612 buf[rc] = 0;
613 return (strstr(buf, "[never]") == NULL);
614 }
615
616 #define UCS_PROC_SYS_SHMMAX_FILE "/proc/sys/kernel/shmmax"
ucs_get_shmmax()617 size_t ucs_get_shmmax()
618 {
619 ucs_status_t status;
620 long size;
621
622 status = ucs_read_file_number(&size, 0, UCS_PROC_SYS_SHMMAX_FILE);
623 if (status != UCS_OK) {
624 ucs_warn("failed to read %s:%m", UCS_PROC_SYS_SHMMAX_FILE);
625 return 0;
626 }
627
628 return size;
629 }
630
ucs_sysv_shmget_error_check_ENOSPC(size_t alloc_size,const struct shminfo * ipc_info,char * buf,size_t max)631 static void ucs_sysv_shmget_error_check_ENOSPC(size_t alloc_size,
632 const struct shminfo *ipc_info,
633 char *buf, size_t max)
634 {
635 unsigned long new_used_ids;
636 unsigned long new_shm_tot;
637 struct shm_info shm_info;
638 char *p, *endp;
639 int ret;
640
641 p = buf;
642 endp = p + max;
643
644 ret = shmctl(0, SHM_INFO, (struct shmid_ds *)&shm_info);
645 if (ret >= 0) {
646 return;
647 }
648
649 new_used_ids = shm_info.used_ids;
650 if (new_used_ids > ipc_info->shmmni) {
651 snprintf(p, endp - p,
652 ", total number of segments in the system (%lu) would exceed the"
653 " limit in /proc/sys/kernel/shmmni (=%lu)",
654 new_used_ids, ipc_info->shmmni);
655 p += strlen(p);
656 }
657
658 new_shm_tot = shm_info.shm_tot +
659 (alloc_size + ucs_get_page_size() - 1) / ucs_get_page_size();
660 if (new_shm_tot > ipc_info->shmall) {
661 snprintf(p, endp - p,
662 ", total shared memory pages in the system (%lu) would exceed the"
663 " limit in /proc/sys/kernel/shmall (=%lu)",
664 new_shm_tot, ipc_info->shmall);
665 }
666 }
667
ucs_sys_get_proc_cap(uint32_t * effective)668 ucs_status_t ucs_sys_get_proc_cap(uint32_t *effective)
669 {
670 #if HAVE_SYS_CAPABILITY_H
671 cap_user_header_t hdr = ucs_alloca(sizeof(*hdr));
672 cap_user_data_t data = ucs_alloca(sizeof(*data) * _LINUX_CAPABILITY_U32S_3);
673 int ret;
674
675 hdr->pid = 0; /* current thread */
676 hdr->version = _LINUX_CAPABILITY_VERSION_3;
677 ret = capget(hdr, data);
678 if (ret) {
679 ucs_debug("capget(pid=%d version=0x%x) failed: %m", hdr->pid,
680 hdr->version);
681 return UCS_ERR_IO_ERROR;
682
683 }
684
685 *effective = data->effective;
686 return UCS_OK;
687 #else
688 return UCS_ERR_UNSUPPORTED;
689 #endif
690 }
691
ucs_sysv_shmget_error_check_EPERM(int flags,char * buf,size_t max)692 static void ucs_sysv_shmget_error_check_EPERM(int flags, char *buf, size_t max)
693 {
694 #if HAVE_SYS_CAPABILITY_H
695 ucs_status_t status;
696 uint32_t ecap;
697
698 UCS_STATIC_ASSERT(CAP_IPC_LOCK < 32);
699 status = ucs_sys_get_proc_cap(&ecap);
700 if ((status == UCS_OK) && !(ecap & UCS_BIT(CAP_IPC_LOCK))) {
701 /* detected missing CAP_IPC_LOCK */
702 snprintf(buf, max, ", CAP_IPC_LOCK privilege is needed for SHM_HUGETLB");
703 return;
704 }
705 #endif
706
707 snprintf(buf, max,
708 ", please check for CAP_IPC_LOCK privilege for using SHM_HUGETLB");
709 }
710
ucs_sysv_shmget_format_error(size_t alloc_size,int flags,const char * alloc_name,int sys_errno,char * buf,size_t max)711 static void ucs_sysv_shmget_format_error(size_t alloc_size, int flags,
712 const char *alloc_name, int sys_errno,
713 char *buf, size_t max)
714 {
715 struct shminfo ipc_info;
716 char *p, *endp, *errp;
717 int ret;
718
719 buf[0] = '\0';
720 p = buf;
721 endp = p + max;
722
723 snprintf(p, endp - p, "shmget(size=%zu flags=0x%x) for %s failed: %s",
724 alloc_size, flags, alloc_name, strerror(sys_errno));
725 p += strlen(p);
726 errp = p; /* save current string pointer to detect if anything was added */
727
728 ret = shmctl(0, IPC_INFO, (struct shmid_ds *)&ipc_info);
729 if (ret >= 0) {
730 if ((sys_errno == EINVAL) && (alloc_size > ipc_info.shmmax)) {
731 snprintf(p, endp - p,
732 ", allocation size exceeds /proc/sys/kernel/shmmax (=%zu)",
733 ipc_info.shmmax);
734 p += strlen(p);
735 }
736
737 if (sys_errno == ENOSPC) {
738 ucs_sysv_shmget_error_check_ENOSPC(alloc_size, &ipc_info, p, endp - p);
739 p += strlen(p);
740 }
741 }
742
743 if (sys_errno == EPERM) {
744 ucs_sysv_shmget_error_check_EPERM(flags, p, endp - p);
745 p += strlen(p);
746 }
747
748 /* default error message if no useful information was added to the string */
749 if (p == errp) {
750 snprintf(p, endp - p, ", please check shared memory limits by 'ipcs -l'");
751 }
752 }
753
ucs_sysv_alloc(size_t * size,size_t max_size,void ** address_p,int flags,const char * alloc_name,int * shmid)754 ucs_status_t ucs_sysv_alloc(size_t *size, size_t max_size, void **address_p,
755 int flags, const char *alloc_name, int *shmid)
756 {
757 char error_string[256];
758 #ifdef SHM_HUGETLB
759 ssize_t huge_page_size;
760 #endif
761 size_t alloc_size;
762 int sys_errno;
763 void *ptr;
764 int ret;
765
766 #ifdef SHM_HUGETLB
767 if (flags & SHM_HUGETLB) {
768 huge_page_size = ucs_get_huge_page_size();
769 if (huge_page_size <= 0) {
770 ucs_debug("huge pages are not supported on the system");
771 return UCS_ERR_NO_MEMORY; /* Huge pages not supported */
772 }
773
774 alloc_size = ucs_align_up(*size, huge_page_size);
775 } else
776 #endif
777 {
778 alloc_size = ucs_align_up(*size, ucs_get_page_size());
779 }
780
781 if (alloc_size >= max_size) {
782 return UCS_ERR_EXCEEDS_LIMIT;
783 }
784
785 flags |= IPC_CREAT | SHM_R | SHM_W;
786 *shmid = shmget(IPC_PRIVATE, alloc_size, flags);
787 if (*shmid < 0) {
788 sys_errno = errno;
789 ucs_sysv_shmget_format_error(alloc_size, flags, alloc_name, sys_errno,
790 error_string, sizeof(error_string));
791 switch (sys_errno) {
792 case ENOMEM:
793 case EPERM:
794 #ifdef SHM_HUGETLB
795 if (!(flags & SHM_HUGETLB))
796 #endif
797 {
798 ucs_error("%s", error_string);
799 }
800 return UCS_ERR_NO_MEMORY;
801 case ENOSPC:
802 case EINVAL:
803 ucs_error("%s", error_string);
804 return UCS_ERR_NO_MEMORY;
805 default:
806 ucs_error("%s", error_string);
807 return UCS_ERR_SHMEM_SEGMENT;
808 }
809 }
810
811 /* Attach segment */
812 if (*address_p) {
813 ptr = shmat(*shmid, *address_p, SHM_REMAP);
814 } else {
815 ptr = shmat(*shmid, NULL, 0);
816 }
817
818 /* Remove segment, the attachment keeps a reference to the mapping */
819 /* FIXME having additional attaches to a removed segment is not portable
820 * behavior */
821 ret = shmctl(*shmid, IPC_RMID, NULL);
822 if (ret != 0) {
823 ucs_warn("shmctl(IPC_RMID, shmid=%d) returned %d: %m", *shmid, ret);
824 }
825
826 /* Check if attachment was successful */
827 if (ptr == (void*)-1) {
828 if (errno == ENOMEM) {
829 return UCS_ERR_NO_MEMORY;
830 } else if (RUNNING_ON_VALGRIND && (errno == EINVAL)) {
831 return UCS_ERR_NO_MEMORY;
832 } else {
833 ucs_error("shmat(shmid=%d) returned unexpected error: %m", *shmid);
834 return UCS_ERR_SHMEM_SEGMENT;
835 }
836 }
837
838 ucs_memtrack_allocated(ptr, alloc_size UCS_MEMTRACK_VAL);
839 *address_p = ptr;
840 *size = alloc_size;
841 return UCS_OK;
842 }
843
ucs_sysv_free(void * address)844 ucs_status_t ucs_sysv_free(void *address)
845 {
846 int ret;
847
848 ucs_memtrack_releasing(address);
849 ret = shmdt(address);
850 if (ret) {
851 ucs_warn("Unable to detach shared memory segment at %p: %m", address);
852 return UCS_ERR_INVALID_PARAM;
853 }
854
855 return UCS_OK;
856 }
857
ucs_mmap_alloc(size_t * size,void ** address_p,int flags UCS_MEMTRACK_ARG)858 ucs_status_t ucs_mmap_alloc(size_t *size, void **address_p,
859 int flags UCS_MEMTRACK_ARG)
860 {
861 size_t alloc_length;
862 void *addr;
863
864 alloc_length = ucs_align_up_pow2(*size, ucs_get_page_size());
865
866 addr = ucs_mmap(*address_p, alloc_length, PROT_READ | PROT_WRITE,
867 MAP_PRIVATE | MAP_ANON | flags, -1, 0 UCS_MEMTRACK_VAL);
868 if (addr == MAP_FAILED) {
869 return UCS_ERR_NO_MEMORY;
870 }
871
872 *size = alloc_length;
873 *address_p = addr;
874 return UCS_OK;
875 }
876
ucs_mmap_free(void * address,size_t length)877 ucs_status_t ucs_mmap_free(void *address, size_t length)
878 {
879 int ret;
880 size_t alloc_length;
881
882 alloc_length = ucs_align_up_pow2(length, ucs_get_page_size());
883
884 ret = ucs_munmap(address, alloc_length);
885 if (ret != 0) {
886 ucs_warn("munmap(address=%p, length=%zu) failed: %m", address, length);
887 return UCS_ERR_INVALID_PARAM;
888 }
889 return UCS_OK;
890 }
891
892 typedef struct {
893 unsigned long start;
894 unsigned long end;
895 int prot;
896 int found;
897 } ucs_get_mem_prot_ctx_t;
898
ucs_get_mem_prot_cb(void * arg,void * addr,size_t length,int prot,const char * path)899 static int ucs_get_mem_prot_cb(void *arg, void *addr, size_t length, int prot,
900 const char *path)
901 {
902 ucs_get_mem_prot_ctx_t *ctx = arg;
903 unsigned long seg_start = (uintptr_t)addr;
904 unsigned long seg_end = (uintptr_t)addr + length;
905
906 if (ctx->start < seg_start) {
907 ucs_trace("address 0x%lx is before next mapping 0x%lx..0x%lx", ctx->start,
908 seg_start, seg_end);
909 return 1;
910 } else if (ctx->start < seg_end) {
911 ucs_trace("range 0x%lx..0x%lx overlaps with mapping 0x%lx..0x%lx prot 0x%x",
912 ctx->start, ctx->end, seg_start, seg_end, prot);
913
914 if (!ctx->found) {
915 /* first segment sets protection flags */
916 ctx->prot = prot;
917 ctx->found = 1;
918 } else {
919 /* subsequent segments update protection flags */
920 ctx->prot &= prot;
921 }
922
923 if (ctx->end <= seg_end) {
924 /* finished going over entire memory region */
925 return 1;
926 }
927
928 /* continue from the end of current segment */
929 ctx->start = seg_end;
930 }
931 return 0;
932 }
933
ucs_get_mem_prot(unsigned long start,unsigned long end)934 int ucs_get_mem_prot(unsigned long start, unsigned long end)
935 {
936 ucs_get_mem_prot_ctx_t ctx = { start, end, PROT_NONE, 0 };
937 ucm_parse_proc_self_maps(ucs_get_mem_prot_cb, &ctx);
938 return ctx.prot;
939 }
940
ucs_get_process_cmdline()941 const char* ucs_get_process_cmdline()
942 {
943 static char cmdline[1024] = {0};
944 static int initialized = 0;
945 ssize_t len;
946 int i;
947
948 if (!initialized) {
949 len = ucs_read_file(cmdline, sizeof(cmdline), 1, "/proc/self/cmdline");
950 for (i = 0; i < len; ++i) {
951 if (cmdline[i] == '\0') {
952 cmdline[i] = ' ';
953 }
954 }
955 initialized = 1;
956 }
957 return cmdline;
958 }
959
960 static ucs_status_t
ucs_sys_enum_pfn_internal(int pagemap_fd,unsigned start_page,uint64_t * data,uintptr_t address,unsigned page_count,ucs_sys_enum_pfn_cb_t cb,void * ctx)961 ucs_sys_enum_pfn_internal(int pagemap_fd, unsigned start_page, uint64_t *data,
962 uintptr_t address, unsigned page_count,
963 ucs_sys_enum_pfn_cb_t cb, void *ctx)
964 {
965 off_t offset;
966 ssize_t ret;
967 size_t len;
968 unsigned i;
969
970 offset = ((address / ucs_get_page_size()) + start_page) * sizeof(*data);
971 len = page_count * sizeof(*data);
972 ret = pread(pagemap_fd, data, len, offset);
973 if (ret < 0) {
974 ucs_warn("pread(file=%s offset=%zu) failed: %m", ucs_pagemap_file, offset);
975 return UCS_ERR_IO_ERROR;
976 }
977
978 for (i = 0; i < ret / sizeof(*data); i++) {
979 if (!(data[i] & UCS_BIT(63))) {
980 ucs_trace("address 0x%lx not present",
981 address + (ucm_get_page_size() * (i + start_page)));
982 return UCS_ERR_IO_ERROR;
983 }
984
985 cb(i + start_page, data[i] & UCS_MASK(55), ctx);
986 }
987
988 return UCS_OK;
989 }
990
ucs_sys_enum_pfn(uintptr_t address,unsigned page_count,ucs_sys_enum_pfn_cb_t cb,void * ctx)991 ucs_status_t ucs_sys_enum_pfn(uintptr_t address, unsigned page_count,
992 ucs_sys_enum_pfn_cb_t cb, void *ctx)
993 {
994 /* by default use 1K buffer on stack */
995 const int UCS_SYS_ENUM_PFN_ELEM_CNT = ucs_min(128, UCS_ALLOCA_MAX_SIZE /
996 sizeof(uint64_t));
997 static int initialized = 0;
998 ucs_status_t status = UCS_OK;
999 static int pagemap_fd;
1000 uint64_t *data;
1001 unsigned page_num;
1002
1003 if (!initialized) {
1004 pagemap_fd = open(ucs_pagemap_file, O_RDONLY);
1005 if (pagemap_fd < 0) {
1006 ucs_warn("failed to open %s: %m", ucs_pagemap_file);
1007 }
1008 initialized = 1;
1009 }
1010
1011 if (pagemap_fd < 0) {
1012 return UCS_ERR_IO_ERROR; /* could not open file */
1013 }
1014
1015 data = ucs_alloca(ucs_min(UCS_SYS_ENUM_PFN_ELEM_CNT, page_count) *
1016 sizeof(*data));
1017
1018 for (page_num = 0; (page_num < page_count) && (status == UCS_OK);
1019 page_num += UCS_SYS_ENUM_PFN_ELEM_CNT) {
1020 status = ucs_sys_enum_pfn_internal(pagemap_fd, page_num, data, address,
1021 ucs_min(UCS_SYS_ENUM_PFN_ELEM_CNT,
1022 page_count - page_num),
1023 cb, ctx);
1024 }
1025
1026 return status;
1027 }
1028
ucs_sys_get_pfn_cb(unsigned page_number,unsigned long pfn,void * ctx)1029 static void ucs_sys_get_pfn_cb(unsigned page_number, unsigned long pfn,
1030 void *ctx)
1031 {
1032 ((unsigned long*)ctx)[page_number] = pfn;
1033 }
1034
ucs_sys_get_pfn(uintptr_t address,unsigned page_count,unsigned long * data)1035 ucs_status_t ucs_sys_get_pfn(uintptr_t address, unsigned page_count,
1036 unsigned long *data)
1037 {
1038 return ucs_sys_enum_pfn(address, page_count, ucs_sys_get_pfn_cb, data);
1039 }
1040
ucs_sys_fcntl_modfl(int fd,int add,int rem)1041 ucs_status_t ucs_sys_fcntl_modfl(int fd, int add, int rem)
1042 {
1043 int oldfl, ret;
1044
1045 oldfl = fcntl(fd, F_GETFL);
1046 if (oldfl < 0) {
1047 ucs_error("fcntl(fd=%d, F_GETFL) returned %d: %m", fd, oldfl);
1048 return UCS_ERR_IO_ERROR;
1049 }
1050
1051 ret = fcntl(fd, F_SETFL, (oldfl | add) & ~rem);
1052 if (ret < 0) {
1053 ucs_error("fcntl(fd=%d, F_SETFL) returned %d: %m", fd, ret);
1054 return UCS_ERR_IO_ERROR;
1055 }
1056
1057 return UCS_OK;
1058 }
1059
ucs_get_tid(void)1060 pid_t ucs_get_tid(void)
1061 {
1062 #ifdef SYS_gettid
1063 return syscall(SYS_gettid);
1064 #elif defined(HAVE_SYS_THR_H)
1065 long id;
1066
1067 thr_self(&id);
1068 return (id);
1069 #else
1070 #error "Port me"
1071 #endif
1072 }
1073
ucs_tgkill(int tgid,int tid,int sig)1074 int ucs_tgkill(int tgid, int tid, int sig)
1075 {
1076 #ifdef SYS_tgkill
1077 return syscall(SYS_tgkill, tgid, tid, sig);
1078 #elif defined(HAVE_SYS_THR_H)
1079 return (thr_kill2(tgid, tid, sig));
1080 #else
1081 #error "Port me"
1082 #endif
1083 }
1084
ucs_get_cpuinfo_clock_freq(const char * header,double scale)1085 double ucs_get_cpuinfo_clock_freq(const char *header, double scale)
1086 {
1087 double value = 0.0;
1088 double m;
1089 int rc;
1090 FILE* f;
1091 char buf[256];
1092 char fmt[256];
1093 int warn;
1094
1095 f = fopen("/proc/cpuinfo","r");
1096 if (!f) {
1097 return 0.0;
1098 }
1099
1100 snprintf(fmt, sizeof(fmt), "%s : %%lf ", header);
1101
1102 warn = 0;
1103 while (fgets(buf, sizeof(buf), f)) {
1104
1105 rc = sscanf(buf, fmt, &m);
1106 if (rc != 1) {
1107 continue;
1108 }
1109
1110 if (value == 0.0) {
1111 value = m;
1112 continue;
1113 }
1114
1115 if (value != m) {
1116 value = ucs_max(value,m);
1117 warn = 1;
1118 }
1119 }
1120 fclose(f);
1121
1122 if (warn) {
1123 ucs_debug("Conflicting CPU frequencies detected, using: %.2f", value);
1124 }
1125
1126 return value * scale;
1127 }
1128
ucs_sys_realloc(void * old_ptr,size_t old_length,size_t new_length)1129 void *ucs_sys_realloc(void *old_ptr, size_t old_length, size_t new_length)
1130 {
1131 void *ptr;
1132
1133 new_length = ucs_align_up_pow2(new_length, ucs_get_page_size());
1134 if (old_ptr == NULL) {
1135 /* Note: Must pass the 0 offset as "long", otherwise it will be
1136 * partially undefined when converted to syscall arguments */
1137 ptr = (void*)syscall(__NR_mmap, NULL, new_length, PROT_READ|PROT_WRITE,
1138 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0ul);
1139 if (ptr == MAP_FAILED) {
1140 ucs_log_fatal_error("mmap(NULL, %zu, READ|WRITE, PRIVATE|ANON) failed: %m",
1141 new_length);
1142 return NULL;
1143 }
1144 } else {
1145 old_length = ucs_align_up_pow2(old_length, ucs_get_page_size());
1146 ptr = (void*)syscall(__NR_mremap, old_ptr, old_length, new_length,
1147 MREMAP_MAYMOVE);
1148 if (ptr == MAP_FAILED) {
1149 ucs_log_fatal_error("mremap(%p, %zu, %zu, MAYMOVE) failed: %m",
1150 old_ptr, old_length, new_length);
1151 return NULL;
1152 }
1153 }
1154
1155 return ptr;
1156 }
1157
ucs_sys_free(void * ptr,size_t length)1158 void ucs_sys_free(void *ptr, size_t length)
1159 {
1160 int ret;
1161
1162 if (ptr != NULL) {
1163 length = ucs_align_up_pow2(length, ucs_get_page_size());
1164 ret = syscall(__NR_munmap, ptr, length);
1165 if (ret) {
1166 ucs_log_fatal_error("munmap(%p, %zu) failed: %m", ptr, length);
1167 }
1168 }
1169 }
1170
ucs_make_affinity_str(const ucs_sys_cpuset_t * cpuset,char * str,size_t len)1171 char* ucs_make_affinity_str(const ucs_sys_cpuset_t *cpuset, char *str, size_t len)
1172 {
1173 int i = 0, prev = -1;
1174 char *p = str;
1175
1176 for (i = 0; i < CPU_SETSIZE; i++) {
1177 if (CPU_ISSET(i, cpuset)) {
1178 if (prev < 0) {
1179 prev = i;
1180 }
1181 } else {
1182 if (prev >= 0) {
1183 if (prev == i - 1) {
1184 p += snprintf(p, str + len - p, "%d,", prev);
1185 } else {
1186 p += snprintf(p, str + len - p, "%d-%d,", prev, i - 1);
1187 }
1188 }
1189 if (p > str + len) {
1190 p = str + len - 4;
1191 while (*p != ',') {
1192 p--;
1193 }
1194 sprintf(p, "...");
1195 return str;
1196 }
1197 prev = -1;
1198 }
1199 }
1200
1201 *(--p) = 0;
1202 return str;
1203 }
1204
ucs_sys_setaffinity(ucs_sys_cpuset_t * cpuset)1205 int ucs_sys_setaffinity(ucs_sys_cpuset_t *cpuset)
1206 {
1207 int ret;
1208
1209 #if defined(HAVE_SCHED_SETAFFINITY)
1210 ret = sched_setaffinity(0, sizeof(*cpuset), cpuset);
1211 #elif defined(HAVE_CPUSET_SETAFFINITY)
1212 ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(),
1213 sizeof(*cpuset), cpuset);
1214 #else
1215 #error "Port me"
1216 #endif
1217 return ret;
1218 }
1219
ucs_sys_getaffinity(ucs_sys_cpuset_t * cpuset)1220 int ucs_sys_getaffinity(ucs_sys_cpuset_t *cpuset)
1221 {
1222 int ret;
1223
1224 #if defined(HAVE_SCHED_GETAFFINITY)
1225 ret = sched_getaffinity(0, sizeof(*cpuset), cpuset);
1226 #elif defined(HAVE_CPUSET_GETAFFINITY)
1227 ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(),
1228 sizeof(*cpuset), cpuset);
1229 #else
1230 #error "Port me"
1231 #endif
1232 return ret;
1233 }
1234
ucs_sys_cpuset_copy(ucs_cpu_set_t * dst,const ucs_sys_cpuset_t * src)1235 void ucs_sys_cpuset_copy(ucs_cpu_set_t *dst, const ucs_sys_cpuset_t *src)
1236 {
1237 int c;
1238
1239 UCS_CPU_ZERO(dst);
1240 for (c = 0; c < UCS_CPU_SETSIZE; ++c) {
1241 if (CPU_ISSET(c, src)) {
1242 UCS_CPU_SET(c, dst);
1243 }
1244 }
1245 }
1246
ucs_sys_get_ns(ucs_sys_namespace_type_t ns)1247 ucs_sys_ns_t ucs_sys_get_ns(ucs_sys_namespace_type_t ns)
1248 {
1249 char filename[MAXPATHLEN];
1250 int res;
1251 struct stat st;
1252
1253 if (ns >= UCS_SYS_NS_TYPE_LAST) {
1254 return 0;
1255 }
1256
1257 snprintf(filename, sizeof(filename), "%s/%s", UCS_PROCESS_NS_DIR,
1258 ucs_sys_namespace_info[ns].name);
1259
1260 res = stat(filename, &st);
1261 if (res == 0) {
1262 return (ucs_sys_ns_t)st.st_ino;
1263 }
1264
1265 return ucs_sys_namespace_info[ns].dflt;
1266 }
1267
ucs_sys_ns_is_default(ucs_sys_namespace_type_t ns)1268 int ucs_sys_ns_is_default(ucs_sys_namespace_type_t ns)
1269 {
1270 return ucs_sys_get_ns(ns) == ucs_sys_namespace_info[ns].dflt;
1271 }
1272
ucs_sys_get_boot_id(uint64_t * high,uint64_t * low)1273 ucs_status_t ucs_sys_get_boot_id(uint64_t *high, uint64_t *low)
1274 {
1275 static struct {
1276 uint64_t high;
1277 uint64_t low;
1278 } boot_id = {0, 0};
1279
1280 static ucs_init_once_t init_once = UCS_INIT_ONCE_INITIALIZER;
1281 static ucs_status_t status = UCS_ERR_IO_ERROR;
1282 char bootid_str[256];
1283 ssize_t size;
1284 uint32_t v1;
1285 uint16_t v2;
1286 uint16_t v3;
1287 uint16_t v4;
1288 uint8_t v5[6];
1289 int res;
1290 int i;
1291
1292 UCS_INIT_ONCE(&init_once) {
1293 size = ucs_read_file_str(bootid_str, sizeof(bootid_str), 1,
1294 "%s", UCS_PROCESS_BOOTID_FILE);
1295 if (size <= 0) {
1296 continue; /* jump out of INIT_ONCE section */
1297 }
1298
1299 res = sscanf(bootid_str, UCS_PROCESS_BOOTID_FMT,
1300 &v1, &v2, &v3, &v4,
1301 &v5[0], &v5[1], &v5[2],
1302 &v5[3], &v5[4], &v5[5]);
1303 if (res == 10) { /* 10 values should be scanned */
1304 status = UCS_OK;
1305 boot_id.low = ((uint64_t)v1) | ((uint64_t)v2 << 32) |
1306 ((uint64_t)v3 << 48);
1307 boot_id.high = v4;
1308 for (i = 0; i < ucs_array_size(v5); i++) {
1309 boot_id.high |= (uint64_t)v5[i] << (16 + (i * 8));
1310 }
1311 }
1312 }
1313
1314 if (status == UCS_OK) {
1315 *high = boot_id.high;
1316 *low = boot_id.low;
1317 }
1318
1319 return status;
1320 }
1321
ucs_sys_readdir(const char * path,ucs_sys_readdir_cb_t cb,void * ctx)1322 ucs_status_t ucs_sys_readdir(const char *path, ucs_sys_readdir_cb_t cb, void *ctx)
1323 {
1324 ucs_status_t res = 0;
1325 DIR *dir;
1326 struct dirent *entry;
1327 struct dirent *entry_out;
1328 size_t entry_len;
1329
1330 dir = opendir(path);
1331 if (dir == NULL) {
1332 return UCS_ERR_NO_ELEM; /* failed to open directory */
1333 }
1334
1335 entry_len = ucs_offsetof(struct dirent, d_name) +
1336 fpathconf(dirfd(dir), _PC_NAME_MAX) + 1;
1337 entry = (struct dirent*)malloc(entry_len);
1338 if (entry == NULL) {
1339 res = UCS_ERR_NO_MEMORY;
1340 goto failed_no_mem;
1341 }
1342
1343 while (!readdir_r(dir, entry, &entry_out) && (entry_out != NULL)) {
1344 res = cb(entry, ctx);
1345 if (res != UCS_OK) {
1346 break;
1347 }
1348 }
1349
1350 free(entry);
1351 failed_no_mem:
1352 closedir(dir);
1353 return res;
1354 }
1355
ucs_sys_enum_threads_cb(struct dirent * entry,void * _ctx)1356 static ucs_status_t ucs_sys_enum_threads_cb(struct dirent *entry, void *_ctx)
1357 {
1358 ucs_sys_enum_threads_t *ctx = (ucs_sys_enum_threads_t*)_ctx;
1359
1360 return strncmp(entry->d_name, ".", 1) ?
1361 ctx->cb((pid_t)atoi(entry->d_name), ctx->ctx) : 0;
1362 }
1363
ucs_sys_enum_threads(ucs_sys_enum_threads_cb_t cb,void * ctx)1364 ucs_status_t ucs_sys_enum_threads(ucs_sys_enum_threads_cb_t cb, void *ctx)
1365 {
1366 static const char *task_dir = "/proc/self/task";
1367 ucs_sys_enum_threads_t param = {.ctx = ctx, .cb = cb};
1368
1369 return ucs_sys_readdir(task_dir, &ucs_sys_enum_threads_cb, ¶m);
1370 }
1371