[patch] try_to_unuse message -> was buggy shm swap deallocation

Andrea Arcangeli (arcangeli@mbox.queen.it)
Tue, 25 Aug 1998 20:57:58 +0200 (CEST)


I discovered what was causing the try_to_unuse message. It was the shm
swap area that was not deallocated _at_all_ from the swap by
try_to_unuse() at swapoff time. Running swapoff -a while a process was
attacched to the shm swapped out area was causing also unexpected
behaviour I think...

Patch against 2.1.117, seems to cure the problem well.

diff -urN /home/andrea/devel/kernel-tree/linux-2.1.117/CREDITS linux/CREDITS
--- /home/andrea/devel/kernel-tree/linux-2.1.117/CREDITS Tue Aug 25 20:19:55 1998
+++ linux/CREDITS Thu Aug 20 15:04:55 1998
@@ -67,6 +67,7 @@
D: Parport hacker
D: Implemented a workaround for some interrupt buggy printers
D: Author of pscan that helps to fix lp/parport bug
+D: Author of lil (Linux Interrupt Latency benchmark)
D: Various other kernel hacks
S: Via Ciaclini 26
S: Imola 40026
diff -urN /home/andrea/devel/kernel-tree/linux-2.1.117/include/linux/shm.h linux/include/linux/shm.h
--- /home/andrea/devel/kernel-tree/linux-2.1.117/include/linux/shm.h Sat Aug 8 15:44:35 1998
+++ linux/include/linux/shm.h Tue Aug 25 18:11:25 1998
@@ -64,6 +64,7 @@
asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, unsigned long *addr);
asmlinkage int sys_shmdt (char *shmaddr);
asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
+extern void shm_unuse(unsigned long entry, unsigned long page);

#endif /* __KERNEL__ */

diff -urN /home/andrea/devel/kernel-tree/linux-2.1.117/ipc/shm.c linux/ipc/shm.c
--- /home/andrea/devel/kernel-tree/linux-2.1.117/ipc/shm.c Sat Aug 8 15:23:11 1998
+++ linux/ipc/shm.c Tue Aug 25 20:41:41 1998
@@ -3,6 +3,7 @@
* Copyright (C) 1992, 1993 Krishna Balasubramanian
* Many improvements/fixes by Bruno Haible.
* Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
+ * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
*/

#include <linux/errno.h>
@@ -836,4 +837,39 @@
shm_swp++;
shm_rss--;
return 1;
+}
+
+/*
+ * Free the swap entry and set the new pte for the shm page.
+ */
+static void shm_unuse_page(struct shmid_ds *shp, unsigned long idx,
+ unsigned long page, unsigned long entry)
+{
+ pte_t pte;
+
+ pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
+ shp->shm_pages[idx] = pte_val(pte);
+ atomic_inc(&mem_map[MAP_NR(page)].count);
+ shm_rss++;
+
+ swap_free(entry);
+ shm_swp--;
+}
+
+/*
+ * unuse_shm() search for an eventually swapped out shm page.
+ */
+void shm_unuse(unsigned long entry, unsigned long page)
+{
+ int i, n;
+
+ for (i = 0; i < SHMMNI; i++)
+ if (shm_segs[i] != IPC_UNUSED && shm_segs[i] != IPC_NOID)
+ for (n = 0; n < shm_segs[i]->shm_npages; n++)
+ if (shm_segs[i]->shm_pages[n] == entry)
+ {
+ shm_unuse_page(shm_segs[i], n,
+ page, entry);
+ return;
+ }
}
diff -urN /home/andrea/devel/kernel-tree/linux-2.1.117/mm/swapfile.c linux/mm/swapfile.c
--- /home/andrea/devel/kernel-tree/linux-2.1.117/mm/swapfile.c Tue Aug 25 20:19:58 1998
+++ linux/mm/swapfile.c Tue Aug 25 19:39:10 1998
@@ -22,6 +22,7 @@
#include <linux/blkdev.h> /* for blk_size */
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
+#include <linux/shm.h>

#include <asm/bitops.h>
#include <asm/pgtable.h>
@@ -325,6 +326,7 @@
for_each_task(p)
unuse_process(p->mm, entry, page);
read_unlock(&tasklist_lock);
+ shm_unuse(entry, page);
/* Now get rid of the extra reference to the temporary
page we've been using. */
if (PageSwapCache(page_map))

To reproduce the bug in the testing I developed two trivial program. The
first alloc a shm area and wait.

#include <sys/ipc.h>
#include <sys/shm.h>

#define SIZE 1310720

main()
{
int shmid, pid1, pid2;
char *addr, *p;
if ((shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT | 0644)) < 0)
perror("shmget");
if ((addr = shmat(shmid, NULL, 0)) < 0)
perror("shmat");

// pid1 = fork();
// pid2 = fork();
for (p = addr; p < addr + SIZE; p++)
*p = 0;

sleep(1000);
for (p = addr; p < addr + SIZE; p++)
*p = 0;
if (shmdt(addr) < 0)
perror("shmdt");
if (pid1 && pid2)
{
wait(pid1, NULL);
wait(pid2, NULL);
if (shmctl(shmid, IPC_RMID, NULL) < 0)
perror("shmctl");
}
}

This second program help the kernel to swapout the fist program:

main()
{
char *p[20];
int i, j;
for (j=0; j<20; j++)
{
p[j] = (char *) malloc(1000000);
}
for (;;)
for (j=0; j<20; j++)
{
for (i=0; i<1000000; i++)
p[j][i] = 0;
}
}

To reproduce the 2.1.117 shm swap deallocation bug I was used to:

1. launch in background the fist program
2. launch 2 copies of the second one
3. kill the second programs
4. swapoff -a
5. sending to the first program some signal to go ahead and return to
touch the shm area (fg, bg via shell).

Andrea[s] Arcangeli

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.altern.org/andrebalsa/doc/lkml-faq.html