1
2
3
4
5
6
7
8
9#include <linux/malloc.h>
10#include <linux/shm.h>
11#include <linux/swap.h>
12#include <linux/smp_lock.h>
13#include <linux/init.h>
14#include <linux/vmalloc.h>
15
16#include <asm/uaccess.h>
17#include <asm/pgtable.h>
18
19extern int ipcperms (struct ipc_perm *ipcp, short shmflg);
20extern unsigned long get_swap_page (void);
21static int findkey (key_t key);
22static int newseg (key_t key, int shmflg, int size);
23static int shm_map (struct vm_area_struct *shmd);
24static void killseg (int id);
25static void shm_open (struct vm_area_struct *shmd);
26static void shm_close (struct vm_area_struct *shmd);
27static unsigned long shm_nopage(struct vm_area_struct *, unsigned long, int);
28static int shm_swapout(struct vm_area_struct *, struct page *);
29
30static int shm_tot = 0;
31static int shm_rss = 0;
32static int shm_swp = 0;
33static int max_shmid = 0;
34static struct wait_queue *shm_lock = NULL;
35static struct shmid_kernel *shm_segs[SHMMNI];
36
37static unsigned short shm_seq = 0;
38
39
40static ulong swap_attempts = 0;
41static ulong swap_successes = 0;
42static ulong used_segs = 0;
43
44void __init shm_init (void)
45{
46 int id;
47
48 for (id = 0; id < SHMMNI; id++)
49 shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
50 shm_tot = shm_rss = shm_seq = max_shmid = used_segs = 0;
51 shm_lock = NULL;
52 return;
53}
54
55static int findkey (key_t key)
56{
57 int id;
58 struct shmid_kernel *shp;
59
60 for (id = 0; id <= max_shmid; id++) {
61 while ((shp = shm_segs[id]) == IPC_NOID)
62 sleep_on (&shm_lock);
63 if (shp == IPC_UNUSED)
64 continue;
65 if (key == shp->u.shm_perm.key)
66 return id;
67 }
68 return -1;
69}
70
71int shmall = SHMALL;
72int shmall_max = SHMALL;
73
74
75
76
77static int newseg (key_t key, int shmflg, int size)
78{
79 struct shmid_kernel *shp;
80 int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
81 int id, i;
82 pte_t tmp_pte;
83
84 if (size < SHMMIN)
85 return -EINVAL;
86 if (shm_tot + numpages >= shmall)
87 return -ENOSPC;
88 for (id = 0; id < SHMMNI; id++)
89 if (shm_segs[id] == IPC_UNUSED) {
90 shm_segs[id] = (struct shmid_kernel *) IPC_NOID;
91 goto found;
92 }
93 return -ENOSPC;
94
95found:
96 shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_KERNEL);
97 if (!shp) {
98 shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
99 wake_up (&shm_lock);
100 return -ENOMEM;
101 }
102
103 shp->shm_pages = (ulong *) vmalloc (numpages*sizeof(ulong));
104 if (!shp->shm_pages) {
105 shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
106 wake_up (&shm_lock);
107 kfree(shp);
108 return -ENOMEM;
109 }
110
111 pte_clear(&tmp_pte);
112
113 for (i = 0; i < numpages; i++)
114 shp->shm_pages[i] = pte_val(tmp_pte);
115
116 shm_tot += numpages;
117 shp->u.shm_perm.key = key;
118 shp->u.shm_perm.mode = (shmflg & S_IRWXUGO);
119 shp->u.shm_perm.cuid = shp->u.shm_perm.uid = current->euid;
120 shp->u.shm_perm.cgid = shp->u.shm_perm.gid = current->egid;
121 shp->u.shm_perm.seq = shm_seq;
122 shp->u.shm_segsz = size;
123 shp->u.shm_cpid = current->pid;
124 shp->attaches = NULL;
125 shp->u.shm_lpid = shp->u.shm_nattch = 0;
126 shp->u.shm_atime = shp->u.shm_dtime = 0;
127 shp->u.shm_ctime = CURRENT_TIME;
128 shp->shm_npages = numpages;
129
130 if (id > max_shmid)
131 max_shmid = id;
132 shm_segs[id] = shp;
133 used_segs++;
134 wake_up (&shm_lock);
135 return (unsigned int) shp->u.shm_perm.seq * SHMMNI + id;
136}
137
138int shmmax = SHMMAX;
139
140asmlinkage int sys_shmget (key_t key, int size, int shmflg)
141{
142 struct shmid_kernel *shp;
143 int err, id = 0;
144
145 down(¤t->mm->mmap_sem);
146 lock_kernel();
147 if (size < 0 || size > shmmax) {
148 err = -EINVAL;
149 } else if (key == IPC_PRIVATE) {
150 err = newseg(key, shmflg, size);
151 } else if ((id = findkey (key)) == -1) {
152 if (!(shmflg & IPC_CREAT))
153 err = -ENOENT;
154 else
155 err = newseg(key, shmflg, size);
156 } else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {
157 err = -EEXIST;
158 } else {
159 shp = shm_segs[id];
160 if (shp->u.shm_perm.mode & SHM_DEST)
161 err = -EIDRM;
162 else if (size > shp->u.shm_segsz)
163 err = -EINVAL;
164 else if (ipcperms (&shp->u.shm_perm, shmflg))
165 err = -EACCES;
166 else
167 err = (int) shp->u.shm_perm.seq * SHMMNI + id;
168 }
169 unlock_kernel();
170 up(¤t->mm->mmap_sem);
171 return err;
172}
173
174
175
176
177
178static void killseg (int id)
179{
180 struct shmid_kernel *shp;
181 int i, numpages;
182
183 shp = shm_segs[id];
184 if (shp == IPC_NOID || shp == IPC_UNUSED) {
185 printk ("shm nono: killseg called on unused seg id=%d\n", id);
186 return;
187 }
188 shp->u.shm_perm.seq++;
189 shm_seq = (shm_seq+1) % ((unsigned)(1<<31)/SHMMNI);
190 shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
191 used_segs--;
192 if (id == max_shmid)
193 while (max_shmid && (shm_segs[--max_shmid] == IPC_UNUSED));
194 if (!shp->shm_pages) {
195 printk ("shm nono: killseg shp->pages=NULL. id=%d\n", id);
196 return;
197 }
198 numpages = shp->shm_npages;
199 for (i = 0; i < numpages ; i++) {
200 pte_t pte;
201 pte = __pte(shp->shm_pages[i]);
202 if (pte_none(pte))
203 continue;
204 if (pte_present(pte)) {
205 free_page (pte_page(pte));
206 shm_rss--;
207 } else {
208 swap_free(pte_val(pte));
209 shm_swp--;
210 }
211 }
212 vfree(shp->shm_pages);
213 shm_tot -= numpages;
214 kfree(shp);
215 return;
216}
217
218asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
219{
220 struct shmid_ds tbuf;
221 struct shmid_kernel *shp;
222 struct ipc_perm *ipcp;
223 int id, err = -EINVAL;
224
225 lock_kernel();
226 if (cmd < 0 || shmid < 0)
227 goto out;
228 if (cmd == IPC_SET) {
229 err = -EFAULT;
230 if(copy_from_user (&tbuf, buf, sizeof (*buf)))
231 goto out;
232 }
233
234 switch (cmd) {
235 case IPC_INFO:
236 {
237 struct shminfo shminfo;
238 err = -EFAULT;
239 if (!buf)
240 goto out;
241 shminfo.shmmni = SHMMNI;
242 shminfo.shmmax = shmmax;
243 shminfo.shmmin = SHMMIN;
244 shminfo.shmall = shmall;
245 shminfo.shmseg = SHMSEG;
246 if(copy_to_user (buf, &shminfo, sizeof(struct shminfo)))
247 goto out;
248 err = max_shmid;
249 goto out;
250 }
251 case SHM_INFO:
252 {
253 struct shm_info shm_info;
254 err = -EFAULT;
255 shm_info.used_ids = used_segs;
256 shm_info.shm_rss = shm_rss;
257 shm_info.shm_tot = shm_tot;
258 shm_info.shm_swp = shm_swp;
259 shm_info.swap_attempts = swap_attempts;
260 shm_info.swap_successes = swap_successes;
261 if(copy_to_user (buf, &shm_info, sizeof(shm_info)))
262 goto out;
263 err = max_shmid;
264 goto out;
265 }
266 case SHM_STAT:
267 err = -EINVAL;
268 if (shmid > max_shmid)
269 goto out;
270 shp = shm_segs[shmid];
271 if (shp == IPC_UNUSED || shp == IPC_NOID)
272 goto out;
273 if (ipcperms (&shp->u.shm_perm, S_IRUGO))
274 goto out;
275 id = (unsigned int) shp->u.shm_perm.seq * SHMMNI + shmid;
276 err = -EFAULT;
277 if(copy_to_user (buf, &shp->u, sizeof(*buf)))
278 goto out;
279 err = id;
280 goto out;
281 }
282
283 shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
284 err = -EINVAL;
285 if (shp == IPC_UNUSED || shp == IPC_NOID)
286 goto out;
287 err = -EIDRM;
288 if (shp->u.shm_perm.seq != (unsigned int) shmid / SHMMNI)
289 goto out;
290 ipcp = &shp->u.shm_perm;
291
292 switch (cmd) {
293 case SHM_UNLOCK:
294 err = -EPERM;
295 if (!capable(CAP_IPC_LOCK))
296 goto out;
297 err = -EINVAL;
298 if (!(ipcp->mode & SHM_LOCKED))
299 goto out;
300 ipcp->mode &= ~SHM_LOCKED;
301 break;
302 case SHM_LOCK:
303
304
305
306 err = -EPERM;
307 if (!capable(CAP_IPC_LOCK))
308 goto out;
309 err = -EINVAL;
310 if (ipcp->mode & SHM_LOCKED)
311 goto out;
312 ipcp->mode |= SHM_LOCKED;
313 break;
314 case IPC_STAT:
315 err = -EACCES;
316 if (ipcperms (ipcp, S_IRUGO))
317 goto out;
318 err = -EFAULT;
319 if(copy_to_user (buf, &shp->u, sizeof(shp->u)))
320 goto out;
321 break;
322 case IPC_SET:
323 if (current->euid == shp->u.shm_perm.uid ||
324 current->euid == shp->u.shm_perm.cuid ||
325 capable(CAP_SYS_ADMIN)) {
326 ipcp->uid = tbuf.shm_perm.uid;
327 ipcp->gid = tbuf.shm_perm.gid;
328 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
329 | (tbuf.shm_perm.mode & S_IRWXUGO);
330 shp->u.shm_ctime = CURRENT_TIME;
331 break;
332 }
333 err = -EPERM;
334 goto out;
335 case IPC_RMID:
336 if (current->euid == shp->u.shm_perm.uid ||
337 current->euid == shp->u.shm_perm.cuid ||
338 capable(CAP_SYS_ADMIN)) {
339 shp->u.shm_perm.mode |= SHM_DEST;
340 if (shp->u.shm_nattch <= 0)
341 killseg (id);
342 break;
343 }
344 err = -EPERM;
345 goto out;
346 default:
347 err = -EINVAL;
348 goto out;
349 }
350 err = 0;
351out:
352 unlock_kernel();
353 return err;
354}
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369static struct vm_operations_struct shm_vm_ops = {
370 shm_open,
371 shm_close,
372 NULL,
373 NULL,
374 NULL,
375 NULL,
376 shm_nopage,
377 NULL,
378 shm_swapout,
379 NULL
380};
381
382
383static inline void insert_attach (struct shmid_kernel * shp, struct vm_area_struct * shmd)
384{
385 if((shmd->vm_next_share = shp->attaches) != NULL)
386 shp->attaches->vm_pprev_share = &shmd->vm_next_share;
387 shp->attaches = shmd;
388 shmd->vm_pprev_share = &shp->attaches;
389}
390
391
392static inline void remove_attach (struct shmid_kernel * shp, struct vm_area_struct * shmd)
393{
394 if(shmd->vm_next_share)
395 shmd->vm_next_share->vm_pprev_share = shmd->vm_pprev_share;
396 *shmd->vm_pprev_share = shmd->vm_next_share;
397}
398
399
400
401
402
403static int shm_map (struct vm_area_struct *shmd)
404{
405 unsigned long tmp;
406
407
408 do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
409
410
411 tmp = shmd->vm_end - shmd->vm_start;
412 if ((current->rlim[RLIMIT_AS].rlim_cur < RLIM_INFINITY) &&
413 ((current->mm->total_vm << PAGE_SHIFT) + tmp
414 > current->rlim[RLIMIT_AS].rlim_cur))
415 return -ENOMEM;
416 current->mm->total_vm += tmp >> PAGE_SHIFT;
417 insert_vm_struct(current->mm, shmd);
418 merge_segments(current->mm, shmd->vm_start, shmd->vm_end);
419
420 return 0;
421}
422
423
424
425
426asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
427{
428 struct shmid_kernel *shp;
429 struct vm_area_struct *shmd;
430 int err = -EINVAL;
431 unsigned int id;
432 unsigned long addr;
433 unsigned long len;
434
435 down(¤t->mm->mmap_sem);
436 lock_kernel();
437 if (shmid < 0) {
438
439 goto out;
440 }
441
442 shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
443 if (shp == IPC_UNUSED || shp == IPC_NOID) {
444
445 goto out;
446 }
447
448 if (!(addr = (ulong) shmaddr)) {
449 if (shmflg & SHM_REMAP)
450 goto out;
451 err = -ENOMEM;
452 addr = 0;
453 again:
454 if (!(addr = get_unmapped_area(addr, shp->u.shm_segsz)))
455 goto out;
456 if(addr & (SHMLBA - 1)) {
457 addr = (addr + (SHMLBA - 1)) & ~(SHMLBA - 1);
458 goto again;
459 }
460 } else if (addr & (SHMLBA-1)) {
461 if (shmflg & SHM_RND)
462 addr &= ~(SHMLBA-1);
463 else
464 goto out;
465 }
466
467
468
469 len = PAGE_SIZE*shp->shm_npages;
470 err = -EINVAL;
471 if (addr >= TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE - len)
472 goto out;
473
474
475
476
477 if (addr < current->mm->start_stack &&
478 addr > current->mm->start_stack - PAGE_SIZE*(shp->shm_npages + 4))
479 {
480
481 goto out;
482 }
483 if (!(shmflg & SHM_REMAP))
484 if ((shmd = find_vma_intersection(current->mm, addr, addr + shp->u.shm_segsz))) {
485
486
487 goto out;
488 }
489
490 err = -EACCES;
491 if (ipcperms(&shp->u.shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO))
492 goto out;
493 err = -EIDRM;
494 if (shp->u.shm_perm.seq != (unsigned int) shmid / SHMMNI)
495 goto out;
496
497 err = -ENOMEM;
498 shmd = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
499 if (!shmd)
500 goto out;
501 if ((shp != shm_segs[id]) || (shp->u.shm_perm.seq != (unsigned int) shmid / SHMMNI)) {
502 kmem_cache_free(vm_area_cachep, shmd);
503 err = -EIDRM;
504 goto out;
505 }
506
507 shmd->vm_pte = SWP_ENTRY(SHM_SWP_TYPE, id);
508 shmd->vm_start = addr;
509 shmd->vm_end = addr + shp->shm_npages * PAGE_SIZE;
510 shmd->vm_mm = current->mm;
511 shmd->vm_page_prot = (shmflg & SHM_RDONLY) ? PAGE_READONLY : PAGE_SHARED;
512 shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
513 | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
514 | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
515 shmd->vm_file = NULL;
516 shmd->vm_offset = 0;
517 shmd->vm_ops = &shm_vm_ops;
518
519 shp->u.shm_nattch++;
520 if ((err = shm_map (shmd))) {
521 if (--shp->u.shm_nattch <= 0 && shp->u.shm_perm.mode & SHM_DEST)
522 killseg(id);
523 kmem_cache_free(vm_area_cachep, shmd);
524 goto out;
525 }
526
527 insert_attach(shp,shmd);
528
529 shp->u.shm_lpid = current->pid;
530 shp->u.shm_atime = CURRENT_TIME;
531
532 *raddr = addr;
533 err = 0;
534out:
535 unlock_kernel();
536 up(¤t->mm->mmap_sem);
537 return err;
538}
539
540
541static void shm_open (struct vm_area_struct *shmd)
542{
543 unsigned int id;
544 struct shmid_kernel *shp;
545
546 id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
547 shp = shm_segs[id];
548 if (shp == IPC_UNUSED) {
549 printk("shm_open: unused id=%d PANIC\n", id);
550 return;
551 }
552 insert_attach(shp,shmd);
553 shp->u.shm_nattch++;
554 shp->u.shm_atime = CURRENT_TIME;
555 shp->u.shm_lpid = current->pid;
556}
557
558
559
560
561
562
563
564static void shm_close (struct vm_area_struct *shmd)
565{
566 struct shmid_kernel *shp;
567 int id;
568
569
570 id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
571 shp = shm_segs[id];
572 remove_attach(shp,shmd);
573 shp->u.shm_lpid = current->pid;
574 shp->u.shm_dtime = CURRENT_TIME;
575 if (--shp->u.shm_nattch <= 0 && shp->u.shm_perm.mode & SHM_DEST)
576 killseg (id);
577}
578
579
580
581
582
583asmlinkage int sys_shmdt (char *shmaddr)
584{
585 struct vm_area_struct *shmd, *shmdnext;
586
587 down(¤t->mm->mmap_sem);
588 lock_kernel();
589 for (shmd = current->mm->mmap; shmd; shmd = shmdnext) {
590 shmdnext = shmd->vm_next;
591 if (shmd->vm_ops == &shm_vm_ops
592 && shmd->vm_start - shmd->vm_offset == (ulong) shmaddr)
593 do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
594 }
595 unlock_kernel();
596 up(¤t->mm->mmap_sem);
597 return 0;
598}
599
600
601
602
603
604
605
606
607
608static int shm_swapout(struct vm_area_struct * vma, struct page * page)
609{
610 return 0;
611}
612
613
614
615
616static unsigned long shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share)
617{
618 pte_t pte;
619 struct shmid_kernel *shp;
620 unsigned int id, idx;
621
622 id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
623 idx = (address - shmd->vm_start + shmd->vm_offset) >> PAGE_SHIFT;
624
625#ifdef DEBUG_SHM
626 if (id > max_shmid) {
627 printk ("shm_nopage: id=%d too big. proc mem corrupted\n", id);
628 return 0;
629 }
630#endif
631 shp = shm_segs[id];
632
633#ifdef DEBUG_SHM
634 if (shp == IPC_UNUSED || shp == IPC_NOID) {
635 printk ("shm_nopage: id=%d invalid. Race.\n", id);
636 return 0;
637 }
638#endif
639
640
641 if (idx >= shp->shm_npages) {
642 return 0;
643 }
644
645 pte = __pte(shp->shm_pages[idx]);
646 if (!pte_present(pte)) {
647 unsigned long page = get_free_page(GFP_USER);
648 if (!page)
649 return -1;
650 pte = __pte(shp->shm_pages[idx]);
651 if (pte_present(pte)) {
652 free_page (page);
653 goto done;
654 }
655 if (!pte_none(pte)) {
656 rw_swap_page_nocache(READ, pte_val(pte), (char *)page);
657 pte = __pte(shp->shm_pages[idx]);
658 if (pte_present(pte)) {
659 free_page (page);
660 goto done;
661 }
662 swap_free(pte_val(pte));
663 shm_swp--;
664 }
665 shm_rss++;
666 pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
667 shp->shm_pages[idx] = pte_val(pte);
668 } else
669 --current->maj_flt;
670
671done:
672 current->min_flt++;
673 atomic_inc(&mem_map[MAP_NR(pte_page(pte))].count);
674 return pte_page(pte);
675}
676
677
678
679
680static unsigned long swap_id = 0;
681static unsigned long swap_idx = 0;
682
683int shm_swap (int prio, int gfp_mask)
684{
685 pte_t page;
686 struct shmid_kernel *shp;
687 unsigned long swap_nr;
688 unsigned long id, idx;
689 int loop = 0;
690 int counter;
691
692 counter = shm_rss >> prio;
693 if (!counter || !(swap_nr = get_swap_page()))
694 return 0;
695
696 check_id:
697 shp = shm_segs[swap_id];
698 if (shp == IPC_UNUSED || shp == IPC_NOID || shp->u.shm_perm.mode & SHM_LOCKED ) {
699 next_id:
700 swap_idx = 0;
701 if (++swap_id > max_shmid) {
702 swap_id = 0;
703 if (loop)
704 goto failed;
705 loop = 1;
706 }
707 goto check_id;
708 }
709 id = swap_id;
710
711 check_table:
712 idx = swap_idx++;
713 if (idx >= shp->shm_npages)
714 goto next_id;
715
716 page = __pte(shp->shm_pages[idx]);
717 if (!pte_present(page))
718 goto check_table;
719 if ((gfp_mask & __GFP_DMA) && !PageDMA(&mem_map[MAP_NR(pte_page(page))]))
720 goto check_table;
721 swap_attempts++;
722
723 if (--counter < 0) {
724 failed:
725 swap_free (swap_nr);
726 return 0;
727 }
728 if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) != 1)
729 goto check_table;
730 shp->shm_pages[idx] = swap_nr;
731 rw_swap_page_nocache (WRITE, swap_nr, (char *) pte_page(page));
732 free_page(pte_page(page));
733 swap_successes++;
734 shm_swp++;
735 shm_rss--;
736 return 1;
737}
738
739
740
741
742static void shm_unuse_page(struct shmid_kernel *shp, unsigned long idx,
743 unsigned long page, unsigned long entry)
744{
745 pte_t pte;
746
747 pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
748 shp->shm_pages[idx] = pte_val(pte);
749 atomic_inc(&mem_map[MAP_NR(page)].count);
750 shm_rss++;
751
752 swap_free(entry);
753 shm_swp--;
754}
755
756
757
758
759void shm_unuse(unsigned long entry, unsigned long page)
760{
761 int i, n;
762
763 for (i = 0; i < SHMMNI; i++)
764 if (shm_segs[i] != IPC_UNUSED && shm_segs[i] != IPC_NOID)
765 for (n = 0; n < shm_segs[i]->shm_npages; n++)
766 if (shm_segs[i]->shm_pages[n] == entry)
767 {
768 shm_unuse_page(shm_segs[i], n,
769 page, entry);
770 return;
771 }
772}
773