1 /*
2  * Copyright (c) 2015-2016 Mellanox Technologies, Inc.
3  *                         All rights reserved.
4  * Copyright (c) 2017      Research Organization for Information Science
5  *                         and Technology (RIST). All rights reserved.
6  * Copyright (c) 2017-2020 Intel, Inc.  All rights reserved.
7  * $COPYRIGHT$
8  *
9  * Additional copyrights may follow
10  *
11  * $HEADER$
12  */
13 
14 
15 #include <unistd.h>
16 #ifdef HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 
24 #include "src/include/pmix_config.h"
25 #include "include/pmix_common.h"
26 #include "src/include/pmix_globals.h"
27 
28 //#include "pmix_sm.h"
29 #include "src/mca/pshmem/pshmem.h"
30 #include "pshmem_mmap.h"
31 
32 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
33 #    define MAP_ANONYMOUS MAP_ANON
34 #endif /* MAP_ANONYMOUS and MAP_ANON */
35 
36 static int _mmap_segment_create(pmix_pshmem_seg_t *sm_seg, const char *file_name, size_t size);
37 static int _mmap_segment_attach(pmix_pshmem_seg_t *sm_seg, pmix_pshmem_access_mode_t sm_mode);
38 static int _mmap_segment_detach(pmix_pshmem_seg_t *sm_seg);
39 static int _mmap_segment_unlink(pmix_pshmem_seg_t *sm_seg);
40 
41 pmix_pshmem_base_module_t pmix_mmap_module = {
42     .name = "mmap",
43     .segment_create = _mmap_segment_create,
44     .segment_attach = _mmap_segment_attach,
45     .segment_detach = _mmap_segment_detach,
46     .segment_unlink = _mmap_segment_unlink
47 };
48 
_mmap_segment_create(pmix_pshmem_seg_t * sm_seg,const char * file_name,size_t size)49 static int _mmap_segment_create(pmix_pshmem_seg_t *sm_seg, const char *file_name, size_t size)
50 {
51     int rc = PMIX_SUCCESS;
52     void *seg_addr = MAP_FAILED;
53     pid_t my_pid = getpid();
54 
55     _segment_ds_reset(sm_seg);
56     /* enough space is available, so create the segment */
57     if (-1 == (sm_seg->seg_id = open(file_name, O_CREAT | O_RDWR, 0600))) {
58         pmix_output_verbose(2, pmix_globals.debug_output,
59                 "sys call open(2) fail\n");
60         rc = PMIX_ERROR;
61         goto out;
62     }
63     /* size backing file - note the use of real_size here */
64 #ifdef HAVE_POSIX_FALLOCATE
65     if (0 != (rc = posix_fallocate(sm_seg->seg_id, 0, size))) {
66         pmix_output_verbose(2, pmix_globals.debug_output,
67                 "sys call posix_fallocate(2) fail\n");
68         if (EINVAL == rc) {
69             goto ftruncate;
70         }
71         if (ENOSPC == rc) {
72             rc = PMIX_ERR_OUT_OF_RESOURCE;
73             goto out;
74         } else if (EINVAL != rc && ENOTSUP != rc
75 #ifdef EOPNOTSUPP
76                             && EOPNOTSUPP != rc
77 #endif
78         ){
79             rc = PMIX_ERROR;
80             goto out;
81         }
82         /* else:
83          * Not supported by OS and/or filesystem.
84          * Must fall-back to ftruncate().
85          */
86     } else {
87         goto map_memory;
88     }
89 #endif
90 ftruncate:
91     if (0 != ftruncate(sm_seg->seg_id, size)) {
92         pmix_output_verbose(2, pmix_globals.debug_output,
93                 "sys call ftruncate(2) fail\n");
94         rc = PMIX_ERROR;
95         goto out;
96     } else {
97         rc = PMIX_SUCCESS;
98     }
99 
100 #ifdef HAVE_POSIX_FALLOCATE
101   map_memory:
102 #endif
103     if (MAP_FAILED == (seg_addr = mmap(NULL, size,
104                                        PROT_READ | PROT_WRITE, MAP_SHARED,
105                                        sm_seg->seg_id, 0))) {
106         pmix_output_verbose(2, pmix_globals.debug_output,
107                 "sys call mmap(2) fail\n");
108         rc = PMIX_ERROR;
109         goto out;
110     }
111     sm_seg->seg_cpid = my_pid;
112     sm_seg->seg_size = size;
113     sm_seg->seg_base_addr = (unsigned char *)seg_addr;
114     pmix_strncpy(sm_seg->seg_name, file_name, PMIX_PATH_MAX);
115 
116 out:
117     if (-1 != sm_seg->seg_id) {
118         if (0 != close(sm_seg->seg_id)) {
119             pmix_output_verbose(2, pmix_globals.debug_output,
120                     "sys call close(2) fail\n");
121             rc = PMIX_ERROR;
122          }
123      }
124     /* an error occured, so invalidate the shmem object and munmap if needed */
125     if (PMIX_SUCCESS != rc) {
126         if (MAP_FAILED != seg_addr) {
127             munmap((void *)seg_addr, size);
128         }
129         _segment_ds_reset(sm_seg);
130     }
131     return rc;
132 }
133 
_mmap_segment_attach(pmix_pshmem_seg_t * sm_seg,pmix_pshmem_access_mode_t sm_mode)134 static int _mmap_segment_attach(pmix_pshmem_seg_t *sm_seg, pmix_pshmem_access_mode_t sm_mode)
135 {
136     mode_t mode = O_RDWR;
137     int mmap_prot = PROT_READ | PROT_WRITE;
138 
139     if (sm_mode == PMIX_PSHMEM_RONLY) {
140         mode = O_RDONLY;
141         mmap_prot = PROT_READ;
142     }
143 
144     if (-1 == (sm_seg->seg_id = open(sm_seg->seg_name, mode))) {
145         return PMIX_ERROR;
146     }
147     if (MAP_FAILED == (sm_seg->seg_base_addr = (unsigned char *)
148                 mmap(NULL, sm_seg->seg_size,
149                     mmap_prot, MAP_SHARED,
150                     sm_seg->seg_id, 0))) {
151         /* mmap failed, so close the file and return NULL - no error check
152          * here because we are already in an error path...
153          */
154         pmix_output_verbose(2, pmix_globals.debug_output,
155                 "sys call mmap(2) fail\n");
156         close(sm_seg->seg_id);
157         return PMIX_ERROR;
158     }
159     /* all is well */
160     /* if close fails here, that's okay.  just let the user know and
161      * continue.  if we got this far, open and mmap were successful...
162      */
163     if (0 != close(sm_seg->seg_id)) {
164         pmix_output_verbose(2, pmix_globals.debug_output,
165                 "sys call close(2) fail\n");
166     }
167     sm_seg->seg_cpid = 0;/* FIXME */
168     return PMIX_SUCCESS;
169 }
170 
_mmap_segment_detach(pmix_pshmem_seg_t * sm_seg)171 static int _mmap_segment_detach(pmix_pshmem_seg_t *sm_seg)
172 {
173     int rc = PMIX_SUCCESS;
174 
175     if (0 != munmap((void *)sm_seg->seg_base_addr, sm_seg->seg_size)) {
176         pmix_output_verbose(2, pmix_globals.debug_output,
177                 "sys call munmap(2) fail\n");
178         rc = PMIX_ERROR;
179     }
180     /* reset the contents of the pmix_sm_seg_t associated with this
181      * shared memory segment.
182      */
183     _segment_ds_reset(sm_seg);
184     return rc;
185 }
186 
_mmap_segment_unlink(pmix_pshmem_seg_t * sm_seg)187 static int _mmap_segment_unlink(pmix_pshmem_seg_t *sm_seg)
188 {
189     if (-1 == unlink(sm_seg->seg_name)) {
190         pmix_output_verbose(2, pmix_globals.debug_output,
191                 "sys call unlink(2) fail\n");
192         return PMIX_ERROR;
193     }
194 
195     sm_seg->seg_id = PMIX_SHMEM_DS_ID_INVALID;
196     return PMIX_SUCCESS;
197 }
198