3 /* Private shm_perm.mode flags, synchronized with NetBSD kernel values */
4 #define SHM_ALLOC 0x0800 /* slot is in use (SHMSEG_ALLOCATED) */
7 struct shmid_ds shmid_ds
;
11 static struct shm_struct shm_list
[SHMMNI
];
12 static unsigned int shm_list_nr
= 0; /* highest in-use slot number plus one */
14 static struct shm_struct
*
15 shm_find_key(key_t key
)
19 if (key
== IPC_PRIVATE
)
22 for (i
= 0; i
< shm_list_nr
; i
++) {
23 if (!(shm_list
[i
].shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
25 if (shm_list
[i
].shmid_ds
.shm_perm
._key
== key
)
32 static struct shm_struct
*
35 struct shm_struct
*shm
;
43 if (!(shm
->shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
45 if (shm
->shmid_ds
.shm_perm
._seq
!= IPCID_TO_SEQ(id
))
51 do_shmget(message
* m
)
53 struct shm_struct
*shm
;
56 size_t size
, old_size
;
60 key
= m
->m_lc_ipc_shmget
.key
;
61 old_size
= size
= m
->m_lc_ipc_shmget
.size
;
62 flag
= m
->m_lc_ipc_shmget
.flag
;
64 if ((shm
= shm_find_key(key
)) != NULL
) {
65 if (!check_perm(&shm
->shmid_ds
.shm_perm
, m
->m_source
, flag
))
67 if ((flag
& IPC_CREAT
) && (flag
& IPC_EXCL
))
69 if (size
&& shm
->shmid_ds
.shm_segsz
< size
)
72 } else { /* no key found */
73 if (!(flag
& IPC_CREAT
))
77 size
= roundup(size
, PAGE_SIZE
);
81 /* Find a free entry. */
82 for (i
= 0; i
< __arraycount(shm_list
); i
++)
83 if (!(shm_list
[i
].shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
85 if (i
== __arraycount(shm_list
))
89 * Allocate memory to share. For now, we store the page
90 * reference as a numerical value so as to avoid issues with
91 * live update. TODO: a proper solution.
93 page
= mmap(0, size
, PROT_READ
| PROT_WRITE
, MAP_ANON
, -1, 0);
94 if (page
== MAP_FAILED
)
96 memset(page
, 0, size
);
98 /* Initialize the entry. */
100 seq
= shm
->shmid_ds
.shm_perm
._seq
;
101 memset(shm
, 0, sizeof(*shm
));
103 shm
->shmid_ds
.shm_perm
._key
= key
;
104 shm
->shmid_ds
.shm_perm
.cuid
=
105 shm
->shmid_ds
.shm_perm
.uid
= getnuid(m
->m_source
);
106 shm
->shmid_ds
.shm_perm
.cgid
=
107 shm
->shmid_ds
.shm_perm
.gid
= getngid(m
->m_source
);
108 shm
->shmid_ds
.shm_perm
.mode
= SHM_ALLOC
| (flag
& ACCESSPERMS
);
109 shm
->shmid_ds
.shm_perm
._seq
= (seq
+ 1) & 0x7fff;
110 shm
->shmid_ds
.shm_segsz
= old_size
;
111 shm
->shmid_ds
.shm_atime
= 0;
112 shm
->shmid_ds
.shm_dtime
= 0;
113 shm
->shmid_ds
.shm_ctime
= clock_time(NULL
);
114 shm
->shmid_ds
.shm_cpid
= getnpid(m
->m_source
);
115 shm
->shmid_ds
.shm_lpid
= 0;
116 shm
->shmid_ds
.shm_nattch
= 0;
117 shm
->page
= (vir_bytes
)page
;
118 shm
->vm_id
= vm_getphys(sef_self(), page
);
120 assert(i
<= shm_list_nr
);
121 if (i
== shm_list_nr
)
125 m
->m_lc_ipc_shmget
.retid
= IXSEQ_TO_IPCID(i
, shm
->shmid_ds
.shm_perm
);
130 do_shmat(message
* m
)
135 struct shm_struct
*shm
;
137 id
= m
->m_lc_ipc_shmat
.id
;
138 addr
= (vir_bytes
)m
->m_lc_ipc_shmat
.addr
;
139 flag
= m
->m_lc_ipc_shmat
.flag
;
141 if (addr
% PAGE_SIZE
) {
143 addr
-= addr
% PAGE_SIZE
;
148 if ((shm
= shm_find_id(id
)) == NULL
)
152 if (flag
& SHM_RDONLY
)
155 mask
= IPC_R
| IPC_W
;
156 if (!check_perm(&shm
->shmid_ds
.shm_perm
, m
->m_source
, mask
))
159 ret
= vm_remap(m
->m_source
, sef_self(), (void *)addr
,
160 (void *)shm
->page
, shm
->shmid_ds
.shm_segsz
);
161 if (ret
== MAP_FAILED
)
164 shm
->shmid_ds
.shm_atime
= clock_time(NULL
);
165 shm
->shmid_ds
.shm_lpid
= getnpid(m
->m_source
);
166 /* nattch is updated lazily */
168 m
->m_lc_ipc_shmat
.retaddr
= ret
;
173 update_refcount_and_destroy(void)
178 for (i
= 0; i
< shm_list_nr
; i
++) {
179 if (!(shm_list
[i
].shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
182 rc
= vm_getrefcount(sef_self(), (void *)shm_list
[i
].page
);
183 if (rc
== (u8_t
)-1) {
184 printf("IPC: can't find physical region.\n");
187 shm_list
[i
].shmid_ds
.shm_nattch
= rc
- 1;
189 if (shm_list
[i
].shmid_ds
.shm_nattch
== 0 &&
190 (shm_list
[i
].shmid_ds
.shm_perm
.mode
& SHM_DEST
)) {
191 munmap((void *)shm_list
[i
].page
,
192 roundup(shm_list
[i
].shmid_ds
.shm_segsz
,
194 /* Mark the entry as free. */
195 shm_list
[i
].shmid_ds
.shm_perm
.mode
&= ~SHM_ALLOC
;
200 * Now that we may have removed an arbitrary set of slots, ensure that
201 * shm_list_nr again equals the highest in-use slot number plus one.
203 while (shm_list_nr
> 0 &&
204 !(shm_list
[shm_list_nr
- 1].shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
209 do_shmdt(message
* m
)
211 struct shm_struct
*shm
;
216 addr
= (vir_bytes
)m
->m_lc_ipc_shmdt
.addr
;
218 if ((vm_id
= vm_getphys(m
->m_source
, (void *)addr
)) == 0)
221 for (i
= 0; i
< shm_list_nr
; i
++) {
224 if (!(shm
->shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
227 if (shm
->vm_id
== vm_id
) {
228 shm
->shmid_ds
.shm_atime
= clock_time(NULL
);
229 shm
->shmid_ds
.shm_lpid
= getnpid(m
->m_source
);
230 /* nattch is updated lazily */
232 vm_unmap(m
->m_source
, (void *)addr
);
236 if (i
== shm_list_nr
)
237 printf("IPC: do_shmdt: ID %lu not found\n", vm_id
);
239 update_refcount_and_destroy();
245 * Fill a shminfo structure with actual information.
248 fill_shminfo(struct shminfo
* sinfo
)
251 memset(sinfo
, 0, sizeof(*sinfo
));
253 sinfo
->shmmax
= (unsigned long)-1;
255 sinfo
->shmmni
= __arraycount(shm_list
);
256 sinfo
->shmseg
= (unsigned long)-1;
257 sinfo
->shmall
= (unsigned long)-1;
261 do_shmctl(message
* m
)
263 struct shmid_ds tmp_ds
;
264 struct shm_struct
*shm
;
265 struct shminfo sinfo
;
266 struct shm_info s_info
;
272 id
= m
->m_lc_ipc_shmctl
.id
;
273 cmd
= m
->m_lc_ipc_shmctl
.cmd
;
274 buf
= (vir_bytes
)m
->m_lc_ipc_shmctl
.buf
;
277 * For stat calls, sure that all information is up-to-date. Since this
278 * may free the slot, do this before mapping from ID to slot below.
280 if (cmd
== IPC_STAT
|| cmd
== SHM_STAT
)
281 update_refcount_and_destroy();
289 if (id
< 0 || (unsigned int)id
>= shm_list_nr
)
292 if (!(shm
->shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
296 if ((shm
= shm_find_id(id
)) == NULL
)
304 /* Check whether the caller has read permission. */
305 if (!check_perm(&shm
->shmid_ds
.shm_perm
, m
->m_source
, IPC_R
))
307 if ((r
= sys_datacopy(SELF
, (vir_bytes
)&shm
->shmid_ds
,
308 m
->m_source
, buf
, sizeof(shm
->shmid_ds
))) != OK
)
311 m
->m_lc_ipc_shmctl
.ret
=
312 IXSEQ_TO_IPCID(id
, shm
->shmid_ds
.shm_perm
);
315 uid
= getnuid(m
->m_source
);
316 if (uid
!= shm
->shmid_ds
.shm_perm
.cuid
&&
317 uid
!= shm
->shmid_ds
.shm_perm
.uid
&& uid
!= 0)
319 if ((r
= sys_datacopy(m
->m_source
, buf
, SELF
,
320 (vir_bytes
)&tmp_ds
, sizeof(tmp_ds
))) != OK
)
322 shm
->shmid_ds
.shm_perm
.uid
= tmp_ds
.shm_perm
.uid
;
323 shm
->shmid_ds
.shm_perm
.gid
= tmp_ds
.shm_perm
.gid
;
324 shm
->shmid_ds
.shm_perm
.mode
&= ~ACCESSPERMS
;
325 shm
->shmid_ds
.shm_perm
.mode
|=
326 tmp_ds
.shm_perm
.mode
& ACCESSPERMS
;
327 shm
->shmid_ds
.shm_ctime
= clock_time(NULL
);
330 uid
= getnuid(m
->m_source
);
331 if (uid
!= shm
->shmid_ds
.shm_perm
.cuid
&&
332 uid
!= shm
->shmid_ds
.shm_perm
.uid
&& uid
!= 0)
334 shm
->shmid_ds
.shm_perm
.mode
|= SHM_DEST
;
335 /* Destroy if possible. */
336 update_refcount_and_destroy();
339 fill_shminfo(&sinfo
);
340 if ((r
= sys_datacopy(SELF
, (vir_bytes
)&sinfo
, m
->m_source
,
341 buf
, sizeof(sinfo
))) != OK
)
344 m
->m_lc_ipc_shmctl
.ret
= shm_list_nr
- 1;
346 m
->m_lc_ipc_shmctl
.ret
= 0;
349 memset(&s_info
, 0, sizeof(s_info
));
350 s_info
.used_ids
= shm_list_nr
;
352 for (i
= 0; i
< shm_list_nr
; i
++)
354 shm_list
[i
].shmid_ds
.shm_segsz
/ PAGE_SIZE
;
355 s_info
.shm_rss
= s_info
.shm_tot
;
357 s_info
.swap_attempts
= 0;
358 s_info
.swap_successes
= 0;
359 if ((r
= sys_datacopy(SELF
, (vir_bytes
)&s_info
, m
->m_source
,
360 buf
, sizeof(s_info
))) != OK
)
363 m
->m_lc_ipc_shmctl
.ret
= shm_list_nr
- 1;
365 m
->m_lc_ipc_shmctl
.ret
= 0;
374 * Return shared memory information for a remote MIB call on the sysvipc_info
375 * node in the kern.ipc subtree. The particular semantics of this call are
376 * tightly coupled to the implementation of the ipcs(1) userland utility.
379 get_shm_mib_info(struct rmib_oldp
* oldp
)
381 struct shm_sysctl_info shmsi
;
382 struct shmid_ds
*shmds
;
388 fill_shminfo(&shmsi
.shminfo
);
391 * As a hackish exception, the requested size may imply that just
392 * general information is to be returned, without throwing an ENOMEM
393 * error because there is no space for full output.
395 if (rmib_getoldlen(oldp
) == sizeof(shmsi
.shminfo
))
396 return rmib_copyout(oldp
, 0, &shmsi
.shminfo
,
397 sizeof(shmsi
.shminfo
));
400 * ipcs(1) blindly expects the returned array to be of size
401 * shminfo.shmmni, using the SHMSEG_ALLOCATED (aka SHM_ALLOC) mode flag
402 * to see whether each entry is valid. If we return a smaller size,
403 * ipcs(1) will access arbitrary memory.
405 assert(shmsi
.shminfo
.shmmni
> 0);
408 return sizeof(shmsi
) + sizeof(shmsi
.shmids
[0]) *
409 (shmsi
.shminfo
.shmmni
- 1);
412 * Copy out entries one by one. For the first entry, copy out the
413 * entire "shmsi" structure. For subsequent entries, reuse the single
414 * embedded 'shmids' element of "shmsi" and copy out only that element.
416 for (i
= 0; i
< shmsi
.shminfo
.shmmni
; i
++) {
417 shmds
= &shm_list
[i
].shmid_ds
;
419 memset(&shmsi
.shmids
[0], 0, sizeof(shmsi
.shmids
[0]));
420 if (i
< shm_list_nr
&& (shmds
->shm_perm
.mode
& SHM_ALLOC
)) {
421 prepare_mib_perm(&shmsi
.shmids
[0].shm_perm
,
423 shmsi
.shmids
[0].shm_segsz
= shmds
->shm_segsz
;
424 shmsi
.shmids
[0].shm_lpid
= shmds
->shm_lpid
;
425 shmsi
.shmids
[0].shm_cpid
= shmds
->shm_cpid
;
426 shmsi
.shmids
[0].shm_atime
= shmds
->shm_atime
;
427 shmsi
.shmids
[0].shm_dtime
= shmds
->shm_dtime
;
428 shmsi
.shmids
[0].shm_ctime
= shmds
->shm_ctime
;
429 shmsi
.shmids
[0].shm_nattch
= shmds
->shm_nattch
;
433 r
= rmib_copyout(oldp
, off
, &shmsi
, sizeof(shmsi
));
435 r
= rmib_copyout(oldp
, off
, &shmsi
.shmids
[0],
436 sizeof(shmsi
.shmids
[0]));
452 printf("key\tid\tpage\n");
453 for (i
= 0; i
< shm_list_nr
; i
++) {
454 if (!(shm_list
[i
].shmid_ds
.shm_perm
.mode
& SHM_ALLOC
))
456 printf("%ld\t%d\t%lx\n",
457 shm_list
[i
].shmid_ds
.shm_perm
._key
,
458 IXSEQ_TO_IPCID(i
, shm_list
[i
].shmid_ds
.shm_perm
),
468 return (shm_list_nr
== 0);