Fixes for issues Coverity has reported (VPP-972)
[vpp.git] / src / vppinfra / linux / mem.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #define _GNU_SOURCE
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <sys/mount.h>
22 #include <sys/mman.h>
23 #include <fcntl.h>
24 #include <linux/mempolicy.h>
25 #include <linux/memfd.h>
26
27 #include <vppinfra/clib.h>
28 #include <vppinfra/mem.h>
29 #include <vppinfra/format.h>
30 #include <vppinfra/clib_error.h>
31 #include <vppinfra/linux/syscall.h>
32 #include <vppinfra/linux/sysfs.h>
33
34 #ifndef F_LINUX_SPECIFIC_BASE
35 #define F_LINUX_SPECIFIC_BASE 1024
36 #endif
37
38 #ifndef F_ADD_SEALS
39 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
40 #define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
41
42 #define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
43 #define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
44 #define F_SEAL_GROW     0x0004  /* prevent file from growing */
45 #define F_SEAL_WRITE    0x0008  /* prevent writes */
46 #endif
47
48 int
49 clib_mem_vm_get_log2_page_size (int fd)
50 {
51   struct stat st = { 0 };
52   if (fstat (fd, &st) == -1)
53     return 0;
54   return min_log2 (st.st_blksize);
55 }
56
57 clib_error_t *
58 clib_mem_vm_ext_alloc (clib_mem_vm_alloc_t * a)
59 {
60   int fd = -1;
61   clib_error_t *err = 0;
62   void *addr = 0;
63   u8 *filename = 0;
64   int mmap_flags = MAP_SHARED;
65   int log2_page_size;
66   int n_pages;
67   int old_mpol = -1;
68   u64 old_mask[16] = { 0 };
69
70   /* save old numa mem policy if needed */
71   if (a->flags & (CLIB_MEM_VM_F_NUMA_PREFER | CLIB_MEM_VM_F_NUMA_FORCE))
72     {
73       int rv;
74       rv =
75         get_mempolicy (&old_mpol, old_mask, sizeof (old_mask) * 8 + 1, 0, 0);
76
77       if (rv == -1)
78         {
79           if ((a->flags & CLIB_MEM_VM_F_NUMA_FORCE) != 0)
80             {
81               err = clib_error_return_unix (0, "get_mempolicy");
82               goto error;
83             }
84           else
85             old_mpol = -1;
86         }
87     }
88
89   /* if we are creating shared segment, we need file descriptor */
90   if (a->flags & CLIB_MEM_VM_F_SHARED)
91     {
92       /* if hugepages are needed we need to create mount point */
93       if (a->flags & CLIB_MEM_VM_F_HUGETLB)
94         {
95           char *mount_dir;
96           char template[] = "/tmp/hugepage_mount.XXXXXX";
97
98           mount_dir = mkdtemp (template);
99           if (mount_dir == 0)
100             return clib_error_return_unix (0, "mkdtemp \'%s\'", template);
101
102           if (mount ("none", (char *) mount_dir, "hugetlbfs", 0, NULL))
103             {
104               err = clib_error_return_unix (0, "mount hugetlb directory '%s'",
105                                             mount_dir);
106               goto error;
107             }
108
109           filename = format (0, "%s/%s%c", mount_dir, a->name, 0);
110
111           if ((fd = open ((char *) filename, O_CREAT | O_RDWR, 0755)) == -1)
112             {
113               err = clib_error_return_unix (0, "open");
114               goto error;
115             }
116           umount2 ((char *) mount_dir, MNT_DETACH);
117           rmdir ((char *) mount_dir);
118           mmap_flags |= MAP_LOCKED;
119         }
120       else
121         {
122           if ((fd = memfd_create (a->name, MFD_ALLOW_SEALING)) == -1)
123             {
124               err = clib_error_return_unix (0, "memfd_create");
125               goto error;
126             }
127
128           if ((fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK)) == -1)
129             {
130               err = clib_error_return_unix (0, "fcntl (F_ADD_SEALS)");
131               goto error;
132             }
133         }
134       log2_page_size = clib_mem_vm_get_log2_page_size (fd);
135     }
136   else                          /* not CLIB_MEM_VM_F_SHARED */
137     {
138       if (a->flags & CLIB_MEM_VM_F_HUGETLB)
139         {
140           mmap_flags |= MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS;
141           log2_page_size = 21;
142         }
143       else
144         {
145           mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
146           log2_page_size = min_log2 (sysconf (_SC_PAGESIZE));
147         }
148     }
149
150   n_pages = ((a->size - 1) >> log2_page_size) + 1;
151
152
153   if (a->flags & CLIB_MEM_VM_F_HUGETLB_PREALLOC)
154     {
155       err = clib_sysfs_prealloc_hugepages (a->numa_node,
156                                            1 << (log2_page_size - 10),
157                                            n_pages);
158       if (err)
159         goto error;
160
161     }
162
163   if (fd != -1)
164     if ((ftruncate (fd, a->size)) == -1)
165       {
166         err = clib_error_return_unix (0, "ftruncate");
167         goto error;
168       }
169
170   if (old_mpol != -1)
171     {
172       int rv;
173       u64 mask[16] = { 0 };
174       mask[0] = 1 << a->numa_node;
175       rv = set_mempolicy (MPOL_BIND, mask, sizeof (mask) * 8 + 1);
176       if (rv)
177         {
178           err = clib_error_return_unix (0, "set_mempolicy");
179           goto error;
180         }
181     }
182
183   addr = mmap (0, a->size, (PROT_READ | PROT_WRITE), mmap_flags, fd, 0);
184   if (addr == MAP_FAILED)
185     {
186       err = clib_error_return_unix (0, "mmap");
187       goto error;
188     }
189
190   /* re-apply ole numa memory policy */
191   if (old_mpol != -1 &&
192       set_mempolicy (old_mpol, old_mask, sizeof (old_mask) * 8 + 1) == -1)
193     {
194       err = clib_error_return_unix (0, "set_mempolicy");
195       goto error;
196     }
197
198   a->log2_page_size = log2_page_size;
199   a->n_pages = n_pages;
200   a->addr = addr;
201   a->fd = fd;
202   goto done;
203
204 error:
205   if (fd != -1)
206     close (fd);
207
208 done:
209   vec_free (filename);
210   return err;
211 }
212
213 u64 *
214 clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages)
215 {
216   int pagesize = sysconf (_SC_PAGESIZE);
217   int fd;
218   int i;
219   u64 *r = 0;
220
221   if ((fd = open ((char *) "/proc/self/pagemap", O_RDONLY)) == -1)
222     return 0;
223
224   for (i = 0; i < n_pages; i++)
225     {
226       u64 seek, pagemap = 0;
227       uword vaddr = pointer_to_uword (mem) + (((u64) i) << log2_page_size);
228       seek = ((u64) vaddr / pagesize) * sizeof (u64);
229       if (lseek (fd, seek, SEEK_SET) != seek)
230         goto done;
231
232       if (read (fd, &pagemap, sizeof (pagemap)) != (sizeof (pagemap)))
233         goto done;
234
235       if ((pagemap & (1ULL << 63)) == 0)
236         goto done;
237
238       pagemap &= pow2_mask (55);
239       vec_add1 (r, pagemap * pagesize);
240     }
241
242 done:
243   close (fd);
244   if (vec_len (r) != n_pages)
245     {
246       vec_free (r);
247       return 0;
248     }
249   return r;
250 }
251
252
253
254 /*
255  * fd.io coding-style-patch-verification: ON
256  *
257  * Local Variables:
258  * eval: (c-set-style "gnu")
259  * End:
260  */