ccanlint: Move ccanlint test options from _info comments to code
[ccan] / ccan / rszshm / rszshm.h
1 /* Licensed under Apache License v2.0 - see LICENSE file for details */
2 #ifndef CCAN_RSZSHM_H
3 #define CCAN_RSZSHM_H
4 #include "config.h"
5
6 #include <stddef.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9
10 /**
11  * struct rszshm_scan - parameters for the free region search
12  * @start: first address to test
13  * @len: size of region to test
14  * @hop: offset of the next test
15  * @iter: number of attempts
16  *
17  * See rszshm_mk for search details.
18  */
19 struct rszshm_scan {
20         void *start;
21         size_t len;
22         size_t hop;
23         unsigned iter;
24 };
25
26 #define KiB (1024UL)
27 #define MiB (KiB*KiB)
28 #define GiB (MiB*KiB)
29 #ifdef __x86_64__
30 #define TiB (GiB*KiB)
31 #define RSZSHM_DFLT_SCAN (struct rszshm_scan) { (void *) (64*TiB), 4*GiB, 1*TiB, 10 }
32 #else
33 #define RSZSHM_DFLT_SCAN (struct rszshm_scan) { (void *) ((1024+512)*MiB), 256*MiB, 256*MiB, 10 }
34 #endif
35
36 /**
37  * struct rszshm_hdr - header describing mapped memory
38  * @flen: length of the shared file mapping
39  * @max: length of the private mapping
40  * @addr: address of the mapping
41  *
42  * The shared region is mapped over the private region.
43  * max is the maximum size the shared region can be extended.
44  * addr and max are set at creation time and do not change.
45  * flen is updated each time the file and shared region is grown.
46  */
47 struct rszshm_hdr {
48         size_t flen;
49         size_t max;
50         void *addr;
51 };
52
53 /**
54  * struct rszshm - handle for a mapped region
55  * @fd: file descriptor of the mapped file
56  * @flen: length of the mapped shared file in this process
57  * @fname: path of the mapped file
58  * @hdr: pointer to the mapped region header
59  * @dat: pointer to the usable space after the header
60  * @cap: length of the usable space after the header
61  *
62  * flen is updated by rszshm_grow, or by rszshm_up.
63  */
64 #define RSZSHM_PATH_MAX 128
65 #define RSZSHM_DFLT_FNAME "/dev/shm/rszshm_XXXXXX/0"
66 struct rszshm {
67         int fd;
68         size_t flen;
69         char fname[RSZSHM_PATH_MAX];
70         struct rszshm_hdr *hdr;
71         void *dat;
72         size_t cap;
73 };
74
75 /**
76  * rszshm_mk - make and mmap a shareable region
77  * @r: pointer to handle
78  * @flen: initial length of shared mapping
79  * @fname: path to file to be created, may be NULL or contain template
80  * @scan: struct specifying search parameters
81  *
82  * The handle pointed to by r is populated by rszshm_mk. flen is increased
83  * by the size of struct rszshm_hdr and rounded up to the next multiple of
84  * page size. If the directory portion of fname ends with XXXXXX, mkdtemp(3)
85  * is used. If fname is NULL, a default path with template is used.
86  *
87  * If rszshm_mk is called with only three arguments, a default scan struct
88  * is used. To supply a struct via compound literal, wrap the argument in
89  * parenthesis to avoid macro failure.
90  *
91  * rszshm_mk attempts to mmap a region of scan.len size at scan.start address.
92  * This is a private anonymous noreserve map used to claim an address space.
93  * If the mapping returns a different address, the region is unmapped, and
94  * another attempt is made at scan.start - scan.hop. If necessary, the next
95  * address tried is scan.start + scan.hop, then scan.start - (2 * scan.hop),
96  * and so on for at most scan.iter iterations. The pattern can be visualized
97  * as a counterclockwise spiral. If no match is found, NULL is returned and
98  * errno is set to ENOSPC.
99  *
100  * When an mmap returns an address matching the requested address, that region
101  * is used. If fname contains a template, mkdtemp(3) is called. A file is
102  * created, and extended to flen bytes. It must not already exist. This file
103  * is mmap'd over the region using MAP_FIXED. The mapping may later be extended
104  * by rszshm_grow consuming more of the claimed address space.
105  *
106  * The initial portion of the mapped file is populated with a struct rszshm_hdr,
107  * and msync called to write out the header.
108  *
109  * Example:
110  *      struct rszshm r, s, t;
111  *
112  *      if (!rszshm_mk(&r, 4*MiB, NULL))
113  *              err(1, "rszshm_mk");
114  *      // map at 0x400000000000
115  *
116  *      if (!rszshm_mk(&s, 4*MiB, "/var/tmp/dat"))
117  *              err(1, "rszshm_mk");
118  *      // map at 0x3f0000000000
119  *
120  *      if (!rszshm_mk(&t, 4*MiB, NULL, ((struct rszshm_scan) { (void *) (48*TiB), 4*GiB, 1*TiB, 10 })))
121  *              err(1, "rszshm_mk");
122  *      // map at 0x300000000000
123  *
124  * Returns: r->dat address on success, NULL on error
125  */
126 void *rszshm_mk(struct rszshm *r, size_t flen, const char *fname, struct rszshm_scan scan);
127 #define __4args(a,b,c,d,...) a, b, c, d
128 #define rszshm_mk(...) rszshm_mk(__4args(__VA_ARGS__, RSZSHM_DFLT_SCAN))
129
130 #if HAVE_STATEMENT_EXPR
131 /**
132  * rszshm_mkm - malloc handle and run rszshm_mk
133  * @r: pointer to handle
134  * @flen: initial length of shared mapping
135  * @fname: path to file to be created, may be NULL or contain template
136  *
137  * Example:
138  *      struct rszshm *r;
139  *
140  *      if (!rszshm_mkm(r, 4*MiB, NULL))
141  *              err(1, "rszshm_mkm");
142  *
143  * Returns: result of rszshm_mk
144  */
145 #define rszshm_mkm(r, fl, fn) ({                        \
146         void *__p = NULL;                               \
147         r = malloc(sizeof(*r));                         \
148         if (r && !(__p = rszshm_mk(r, fl, fn))) {       \
149                 free(r);                                \
150                 r = 0;                                  \
151         }                                               \
152         __p;                                            \
153 })
154 #endif
155
156 /**
157  * rszshm_at - mmap ("attach") an existing shared region
158  * @r: pointer to handle
159  * @fname: path to file
160  *
161  * rszshm_at lets unrelated processes attach an existing shared region.
162  * fname must name a file previously created by rszshm_mk in another process.
163  * Note, fork'd children of the creating process inherit the mapping and
164  * should *not* call rszshm_at.
165  *
166  * rszshm_at opens and reads the header from the file. It makes a private
167  * anonymous noreserve mapping at the address recorded in the header.
168  * If mmap returns an address other than the requested one, munmap
169  * is called, errno is set to ENOSPC, and NULL is returned.
170  *
171  * Once the address space is claimed, the file is mmap'd over the region
172  * using MAP_FIXED. The remaining claimed address space will be used by
173  * later calls to rszshm_grow. Finally, the handle is populated and r->dat
174  * returned.
175  *
176  * Example:
177  *      struct rszshm r;
178  *
179  *      if (!rszshm_at(&r, "/dev/shm/rszshm_LAsEvt/0"))
180  *              err(1, "rszshm_at");
181  *
182  * Returns: r->dat address on success, NULL on error
183  */
184 void *rszshm_at(struct rszshm *r, const char *fname);
185
186 #if HAVE_STATEMENT_EXPR
187 /**
188  * rszshm_atm - malloc handle and run rszshm_at
189  * @r: pointer to handle
190  * @fname: path to file
191  *
192  * Example:
193  *      struct rszshm *r;
194  *
195  *      if (!rszshm_atm(r, "/dev/shm/rszshm_LAsEvt/0"))
196  *              err(1, "rszshm_atm");
197  *
198  * Returns: result of rszshm_at
199  */
200 #define rszshm_atm(r, f) ({                             \
201         void *__p = NULL;                               \
202         r = malloc(sizeof(*r));                         \
203         if (r && !(__p = rszshm_at(r, f))) {            \
204                 free(r);                                \
205                 r = 0;                                  \
206         }                                               \
207         __p;                                            \
208 })
209 #endif
210
211 /**
212  * rszshm_dt - unmap ("detach") shared region
213  * @r: pointer to handle
214  *
215  * Calls msync, munmap, and close. Resets handle values except fname.
216  * (fname is used by rszshm_rm*.)
217  *
218  * Returns: 0 on success, -1 if any call failed
219  */
220 int rszshm_dt(struct rszshm *r);
221
222 /**
223  * rszshm_up - update mapping of shared region
224  * @r: pointer to handle
225  *
226  * Check if flen from the region header matches flen from the handle.
227  * They will diverge when another process runs rszshm_grow.
228  * If they are different, call mmap with the header flen and MAP_FIXED,
229  * and update handle.
230  *
231  * Returns: -1 if mmap fails, 0 for no change, 1 is mapping updated
232  */
233 int rszshm_up(struct rszshm *r);
234 #define rszshm_up(r) (assert(r), (r)->flen == (r)->hdr->flen ? 0 : rszshm_up(r))
235
236 /**
237  * rszshm_grow - double the shared region, conditionally
238  * @r: pointer to handle
239  *
240  * If the region is already at capacity, set errno to ENOMEM, and return -1.
241  *
242  * rszshm_up is called, to see if another process has already grown the region.
243  * If not, a lock is acquired and the check repeated, to avoid races.
244  * The file is extended, and mmap called with MAP_FIXED. The header and handle
245  * are updated.
246  *
247  * Returns: 1 on success, -1 on error
248  */
249 int rszshm_grow(struct rszshm *r);
250
251 /**
252  * rszshm_unlink - unlink shared file
253  * @r: pointer to handle
254  *
255  * Returns: result of unlink
256  */
257 int rszshm_unlink(struct rszshm *r);
258
259 /**
260  * rszshm_rmdir - rmdir of fname directory
261  * @r: pointer to handle
262  *
263  * Returns: result of rmdir
264  */
265 int rszshm_rmdir(struct rszshm *r);
266
267 #if HAVE_STATEMENT_EXPR
268 /**
269  * rszshm_rm - remove file and directory
270  * @r: pointer to handle
271  *
272  * Calls rszshm_unlink and rszshm_rmdir.
273  *
274  * Returns: 0 on success, -1 on error
275  */
276 #define rszshm_rm(r) ({                         \
277         int __ret;                              \
278         assert(r);                              \
279         __ret = rszshm_unlink(r);               \
280         if (__ret == 0)                         \
281                 __ret = rszshm_rmdir(r);        \
282         __ret;                                  \
283 })
284 #endif
285
286 #if HAVE_STATEMENT_EXPR
287 /**
288  * rszshm_free - run rszshm_dt and free malloced handle
289  * @r: pointer to handle
290  *
291  * Returns: result of rszshm_dt
292  */
293 #define rszshm_free(r) ({       \
294         int __i = rszshm_dt(r); \
295         free(r);                \
296         r = 0;                  \
297         __i;                    \
298 })
299 #endif
300
301 #endif