Skip to contentSkip to conversation

cleanup shm handling

PATCH In review Christoph Rohland 3 participants 11 messages Dec 8, 2000 – Dec 13, 2000

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 cleanup shm handling

Hi Linus,

here is my first shot for cleaning up the shm handling. It did survive some basic testing but is not ready for inclusion.

The smp locking needs some review, shmem_file_setup needs cleanup, we probably want to have triple indirect blocks... But the basic concept seems to work.

Oh, and there is this ugly recursion problem in mm.h which made me move typedef swp_entry_t into shmem_fs.h where it does not belong...

Comments are welcome

Christoph

drivers/char/mem.c +1 -1
diff -uNr 4-12-5/drivers/char/mem.c c/drivers/char/mem.c
@@ -438,7 +438,7 @@
 static int mmap_zero(struct file * file, struct vm_area_struct * vma)
 {
 	if (vma->vm_flags & VM_SHARED)
-		return map_zero_setup(vma);
+		return shmem_zero_setup(vma);
 	if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
 		return -EAGAIN;
 	return 0;
include/linux/fs.h +2 -0
diff -uNr 4-12-5/include/linux/fs.h c/include/linux/fs.h
@@ -283,6 +283,7 @@
 #include <linux/efs_fs_i.h>
 #include <linux/coda_fs_i.h>
 #include <linux/romfs_fs_i.h>
+#include <linux/shmem_fs.h>
 #include <linux/smb_fs_i.h>
 #include <linux/hfs_fs_i.h>
 #include <linux/adfs_fs_i.h>
@@ -441,6 +443,7 @@
 		struct ufs_inode_info		ufs_i;
 		struct efs_inode_info		efs_i;
 		struct romfs_inode_info		romfs_i;
+		struct shmem_inode_info		shmem_i;
 		struct coda_inode_info		coda_i;
 		struct smb_inode_info		smbfs_i;
 		struct hfs_inode_info		hfs_i;
include/linux/mm.h +4 -10
diff -uNr 4-12-5/include/linux/mm.h c/include/linux/mm.h
@@ -129,15 +129,6 @@
 };
 
 /*
- * A swap entry has to fit into a "unsigned long", as
- * the entry is hidden in the "index" field of the
- * swapper address space.
- */
-typedef struct {
-	unsigned long val;
-} swp_entry_t;
-
-/*
  * Try to keep the most commonly accessed fields in single cache lines
  * here (16 bytes or greater).  This ordering should be particularly
  * beneficial on 32-bit processors.
@@ -390,7 +381,10 @@
 
 extern void clear_page_tables(struct mm_struct *, unsigned long, int);
 
-extern int map_zero_setup(struct vm_area_struct *);
+int shmem_swapout(struct page * page, struct file *file);
+struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share);
+struct file *shmem_file_setup(struct file *fzero, char * name, loff_t size);
+extern int shmem_zero_setup(struct vm_area_struct *);
 
 extern void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
 extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma);
include/linux/pagemap.h +2 -0
diff -uNr 4-12-5/include/linux/pagemap.h c/include/linux/pagemap.h
@@ -67,6 +67,8 @@
 
 #define page_hash(mapping,index) (page_hash_table+_page_hashfn(mapping,index))
 
+extern struct page * __find_get_page(struct address_space *mapping,
+				     unsigned long offset, struct page **hash);
 extern struct page * __find_lock_page (struct address_space * mapping,
 				unsigned long index, struct page **hash);
 extern void lock_page(struct page *page);
include/linux/shmem_fs.h +30 -0
diff -uNr 4-12-5/include/linux/shmem_fs.h c/include/linux/shmem_fs.h
@@ -0,0 +1,30 @@
+#ifndef __SHMEM_I
+#define __SHMEM_I
+
+/* inode in-kernel data */
+
+#define SHMEM_NR_DIRECT 32
+
+/*
+ * A swap entry has to fit into a "unsigned long", as
+ * the entry is hidden in the "index" field of the
+ * swapper address space.
+ */
+typedef struct {
+	unsigned long val;
+} swp_entry_t;
+
+struct shmem_inode_info {
+	swp_entry_t	i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
+	swp_entry_t	       *i_indirect;	/* indirect blocks	  */
+	swp_entry_t	      **i_double;	/* doubly indirect blocks */
+	struct list_head	list;
+};
+
+struct shmem_sb_info {
+	unsigned long max_blocks;
+	unsigned long nr_blocks;
+	unsigned long nr_swap;
+};
+
+#endif
include/linux/swap.h +2 -0
diff -uNr 4-12-5/include/linux/swap.h c/include/linux/swap.h
@@ -291,6 +291,8 @@
 #define swap_device_lock(p)	spin_lock(&p->sdev_lock)
 #define swap_device_unlock(p)	spin_unlock(&p->sdev_lock)
 
+extern void shmem_unuse(swp_entry_t entry, struct page *page);
+
 #endif /* __KERNEL__*/
 
 #endif /* _LINUX_SWAP_H */
init/main.c +8 -0
diff -uNr 4-12-5/init/main.c c/init/main.c
@@ -55,6 +55,10 @@
 #include <linux/nubus.h>
 #endif
 
+#ifdef CONFIG_X86_REMOTE_DEBUG
+#include <linux/gdb.h>
+#endif
+
 #ifdef CONFIG_ISAPNP
 #include <linux/isapnp.h>
 #endif
@@ -585,6 +589,10 @@
 	 *	make syscalls (and thus be locked).
 	 */
 	smp_init();
+#ifdef CONFIG_X86_REMOTE_DEBUG
+	if (gdb_enter)
+		gdb_hook();		/* right at boot time */
+#endif
 	kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
 	unlock_kernel();
 	current->need_resched = 1;
ipc/shm.c +128 -1202

1,574 lines not on this page · raw message

ipc/util.c +0 -14
diff -uNr 4-12-5/ipc/util.c c/ipc/util.c
@@ -345,11 +345,6 @@
     return;
 }
 
-int shm_swap (int prio, int gfp_mask)
-{
-    return 0;
-}
-
 asmlinkage long sys_semget (key_t key, int nsems, int semflg)
 {
 	return -ENOSYS;
@@ -404,15 +399,6 @@
 asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
 {
 	return -ENOSYS;
-}
-
-void shm_unuse(swp_entry_t entry, struct page *page)
-{
-}
-
-int map_zero_setup(struct vm_area_struct *vma)
-{
-	return -EINVAL;
 }
 
 #endif /* CONFIG_SYSVIPC */
kernel/sysctl.c +6 -0
diff -uNr 4-12-5/kernel/sysctl.c c/kernel/sysctl.c
@@ -63,6 +63,8 @@
 #endif
 #ifdef CONFIG_SYSVIPC
 extern size_t shm_ctlmax;
+extern size_t shm_ctlall;
+extern int shm_ctlmni;
 extern int msg_ctlmax;
 extern int msg_ctlmnb;
 extern int msg_ctlmni;
@@ -208,6 +210,10 @@
 #ifdef CONFIG_SYSVIPC
 	{KERN_SHMMAX, "shmmax", &shm_ctlmax, sizeof (size_t),
 	 0644, NULL, &proc_doulongvec_minmax},
+	{KERN_SHMALL, "shmall", &shm_ctlall, sizeof (size_t),
+	 0644, NULL, &proc_doulongvec_minmax},
+	{KERN_SHMMNI, "shmmni", &shm_ctlmni, sizeof (int),
+	 0644, NULL, &proc_dointvec},
 	{KERN_MSGMAX, "msgmax", &msg_ctlmax, sizeof (int),
 	 0644, NULL, &proc_dointvec},
 	{KERN_MSGMNI, "msgmni", &msg_ctlmni, sizeof (int),
mm/Makefile +2 -1
diff -uNr 4-12-5/mm/Makefile c/mm/Makefile
@@ -10,7 +10,8 @@
 O_TARGET := mm.o
 O_OBJS	 := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \
 	    vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \
-	    page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o
+	    page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o \
+	    shmem.o
 
 ifeq ($(CONFIG_HIGHMEM),y)
 O_OBJS += highmem.o
mm/filemap.c +2 -2
diff -uNr 4-12-5/mm/filemap.c c/mm/filemap.c
@@ -542,8 +542,8 @@
  * a rather lightweight function, finding and getting a reference to a
  * hashed page atomically, waiting for it if it's locked.
  */
-static struct page * __find_get_page(struct address_space *mapping,
-				unsigned long offset, struct page **hash)
+struct page * __find_get_page(struct address_space *mapping,
+			      unsigned long offset, struct page **hash)
 {
 	struct page *page;
 
mm/mmap.c +1 -1
diff -uNr 4-12-5/mm/mmap.c c/mm/mmap.c
@@ -333,7 +333,7 @@
 		if (error)
 			goto unmap_and_free_vma;
 	} else if (flags & MAP_SHARED) {
-		error = map_zero_setup(vma);
+		error = shmem_zero_setup(vma);
 		if (error)
 			goto free_vma;
 	}
mm/shmem.c +718 -0

718 lines not on this page · raw message

mm/swapfile.c +1 -1
diff -uNr 4-12-5/mm/swapfile.c c/mm/swapfile.c
@@ -374,7 +374,7 @@
 		for_each_task(p)
 			unuse_process(p->mm, entry, page);
 		read_unlock(&tasklist_lock);
-		shm_unuse(entry, page);
+		shmem_unuse(entry, page);
 		/* Now get rid of the extra reference to the temporary
                    page we've been using. */
 		if (PageSwapCache(page))

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#1 lore raw
Quoted 3 lines

here is my first shot for cleaning up the shm handling. It did survive some basic testing but is not ready for inclusion.

Can you help me with an SHM related problem?

I'm currently writing a Win32 emulation kernel module to help speed Wine up, and I'm writing the file mapping support stuff at the moment (CreateFileMapping and MapViewOfFile).

I have PE Image mapping just about working (fixups, misaligned file sections and all), but I'm trying to think of a good way of doing anonymous shared mappings without having to hack the main kernel around too much (so far I've only had to add to kernel/ksyms.c).

Is there a reasonable way I could hook into the SHM system to "reserve" a chunk of shared memory of a particular size, and then a second hook by which I can "map" _part_ of that into a process's address space?

Cheers,
David

PS: to anyone else reading this, if you wrote do_generic_file_read(), it rocks!
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#2 lore raw
Quoted 5 lines · David Howells

Can you help me with an SHM related problem?

I'm currently writing a Win32 emulation kernel module to help speed Wine up, and I'm writing the file mapping support stuff at the moment (CreateFileMapping and MapViewOfFile).

These two calls were exactly the reasons to make the shm fs for linux. It makes posix shm possible which provides shm_open an shm_unlink. This together with mmap and frtuncate gives you exactly this functionality.

shm_open and shm_unlink are provided by glibc 2.2. If you don't want to rely on that, you can use open ("/dev/shm/xxx") and hope that the admin configured right.

Quoted 9 lines

I have PE Image mapping just about working (fixups, misaligned file sections and all), but I'm trying to think of a good way of doing anonymous shared mappings without having to hack the main kernel around too much (so far I've only had to add to kernel/ksyms.c).

Is there a reasonable way I could hook into the SHM system to "reserve" a chunk of shared memory of a particular size, and then a second hook by which I can "map" _part_ of that into a process's address space?

Yes:

fd = shm_open ("xxx",...)
ptr = mmap (NULL, size, ..., fd, offset);

No kernel changes necessary on 2.4 any more.

Greetings
Christoph

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#3 lore raw
Quoted 1 line
Quoted 1 line

I'm currently writing a Win32 emulation kernel module to help speed Wine up,

                                           ^^^^^^^^^^^^^
Quoted 2 lines

fd = shm_open ("xxx",...)
ptr = mmap (NULL, size, ..., fd, offset);

I am doing this from within kernel space. I'd like to avoid doing the full open and mmap if possible. I was wondering if there're some shortcuts I could make use of.

David
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#4 lore raw

Hi David,

Quoted 9 lines · David Howells
Quoted 2 lines
Quoted 2 lines

I'm currently writing a Win32 emulation kernel module to help speed Wine up,

                                           ^^^^^^^^^^^^^
Quoted 2 lines

fd = shm_open ("xxx",...)
ptr = mmap (NULL, size, ..., fd, offset);

I am doing this from within kernel space. I'd like to avoid doing the full open and mmap if possible. I was wondering if there're some shortcuts I could make use of.

There will be a

struct file *shmem_file_setup(char * name, loff_t size)

which gives you an open sruct file to an unlinked file of size size. You can then do

down(&current->mm->mmap_sem);
user_addr = (void *) do_mmap (file, addr, size, prot, flags, 0); up(&current->mm->mmap_sem);

with that struct file. You can look at shmget/shmat in ipc/shm.c. They use the same procedure form kernel space.

All this will only work with my patch.

Greetings
Christoph

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#5 lore raw
Quoted 8 lines

There will be a

struct file *shmem_file_setup(char * name, loff_t size)

which gives you an open sruct file to an unlinked file of size size. You can then do

Quoted 6 lines

down(&current->mm->mmap_sem);
user_addr = (void *) do_mmap (file, addr, size, prot, flags, 0); up(&current->mm->mmap_sem);

with that struct file. You can look at shmget/shmat in ipc/shm.c. They use the same procedure form kernel space.

Looks interesting.

There looks to be a logical mapping between CreateFileMapping() + MEM_SHARED and your shmem_file_setup(), as long as anonymously named sections are catered for (not difficult).

There also looks to be a logical mapping between MapViewOfFile() and how you propose do_mmap() should be used.

At the moment, I have to do most of do_mmap for myself when implementing CreateFileMapping() with SEC_IMAGE as a parameter since I need to change the VMA ops table. But that only applies to where a file-backed PE Image (EXE/DLL) is being mapped.

I'm not sure how shared sections in PE Images are handled on all versions of Windows (ie: whether they are actually shared), but I image I could adapt your mechanism for that too. I'd probably just have to create a SHMEM file and load the backing data into it, and then use the SHMEM as the file to attach to the VMA for that section (and then it's someone else's problem as far as swapping is concerned).

If you want a look at what I've done, then you can find it at:

ftp://infradead.org/pub/people/dwh/wineservmod-20001213.tar.bz2

It will hopefully be in CVS on winehq soon as well.

Look at the files called wineservmod/section* these implement the setting up of VMAs in the current processes address space (though don't actually do the page-in as yet).

David
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#6 lore raw

Hi David,

Quoted 5 lines · David Howells

Looks interesting.

There looks to be a logical mapping between CreateFileMapping() + MEM_SHARED and your shmem_file_setup(), as long as anonymously named sections are catered for (not difficult).

Yup.

Quoted 7 lines

There also looks to be a logical mapping between MapViewOfFile() and how you propose do_mmap() should be used.

At the moment, I have to do most of do_mmap for myself when implementing CreateFileMapping() with SEC_IMAGE as a parameter since I need to change the VMA ops table. But that only applies to where a file-backed PE Image (EXE/DLL) is being mapped.

If you want to change the vma ops table you can replace the f_ops table with your own one. SYSV ipc uses this also to be able to catch unmaps.

Quoted 7 lines

I'm not sure how shared sections in PE Images are handled on all versions of Windows (ie: whether they are actually shared), but I image I could adapt your mechanism for that too. I'd probably just have to create a SHMEM file and load the backing data into it, and then use the SHMEM as the file to attach to the VMA for that section (and then it's someone else's problem as far as swapping is concerned).

Oh, that's too much Windows for me ;-)

Greetings
Christoph

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#7 lore raw
Quoted 4 lines

If you want to change the vma ops table you can replace the f_ops table with your own one. SYSV ipc uses this also to be able to catch unmaps.

I'd thought of that, but that means I need to concoct an f_ops table of my own for every f_ops table I might have to override. All I want to do is provide a single VMA ops table (or maybe two), possibly with only a few ops in.

Also, I can't actually go through do_mmap()... PE Image sections in files do not have to be page aligned. If they are, I can call do_mmap() a number of times (once per section), but mostly they're not (they have to be at least 512b aligned though - DOS floppy alignment, I suspect).

Plus if I change the f_ops table, then I affect normal Linuxy processes doing mmap().

Quoted 3 lines
Quoted 1 line

I'm not sure how shared sections in PE Images are handled on all

...
Oh, that's too much Windows for me ;-)

*grin*

David
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#8 lore raw
In reply to Christoph Rohland
Quoted 3 lines · Christoph Rohland

here is my first shot for cleaning up the shm handling. It did survive some basic testing but is not ready for inclusion.

The only comment I have right now is that you probably should not mark the page dirty in "nopage" - theoretically somebody might have a sparse mapping and depend on zero pages for the ones that aren't touched. It's better to delay the dirty marking until swapout() (and write(), when that is implemented), so that we don't needlessly create swap entries for zero pages.

(No, probably nobody does this for traditional shared memory, but I could well imagine mmap() on /dev/zero with most of the pages being read-only).

Other than that the approach at least looks reasonable. And cleaner than what we currently have.

Linus

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#9 lore raw

Hi Linus,

Quoted 11 lines · Linus Torvalds
Quoted 3 lines · Christoph Rohland

here is my first shot for cleaning up the shm handling. It did survive some basic testing but is not ready for inclusion.

The only comment I have right now is that you probably should not mark the page dirty in "nopage" - theoretically somebody might have a sparse mapping and depend on zero pages for the ones that aren't touched. It's better to delay the dirty marking until swapout() (and write(), when that is implemented), so that we don't needlessly create swap entries for zero pages.

OK. I simply copied that from shm.c without thinking. Actually I do not yet understand the implications of it. (I never thought that I would get so deeply involved into these issues and still struggle often with the details)

Quoted 2 lines

Other than that the approach at least looks reasonable. And cleaner than what we currently have.

Only reasonable? :-(

It's what I always thought would be the Right Thing (TM):

1) The mm layer should have the abilty to handle shared anonymous pages
2) To access this we should use the 'everything is a file' mantra which means shm fs
3) sysv shm should only care about handling shm ids (and it special attributes which unfortunately makes shm_vm_operations necessary.)

So how would you improve it conceptually? And where are the implementation flaws?

And compared to all the ipc/shm.c hacks we had so far it looks for me beautiful. (And to make this clear: The main part is not my work. I simply gathered the work of others to make this possible)

Christoph

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/

#10 lore raw
Quoted 14 lines · Christoph Rohland
Quoted 11 lines · Linus Torvalds
Quoted 3 lines · Christoph Rohland

here is my first shot for cleaning up the shm handling. It did survive some basic testing but is not ready for inclusion.

The only comment I have right now is that you probably should not mark the page dirty in "nopage" - theoretically somebody might have a sparse mapping and depend on zero pages for the ones that aren't touched. It's better to delay the dirty marking until swapout() (and write(), when that is implemented), so that we don't needlessly create swap entries for zero pages.

Quoted 4 lines

OK. I simply copied that from shm.c without thinking. Actually I do not yet understand the implications of it. (I never thought that I would get so deeply involved into these issues and still struggle often with the details)

Basically, a clean page will just get dropped by the VM, because it knows it can always re-create the contents.

So let's assume that you do a large mmap(MAP_SHARED) (or IPC shmem mapping), and you populate your VM space with pages by just reading from it. This is not common, but it does happen - things like sparse files where just the fact that nothing has been written to a certain location is information in itself.

Now, assume you run out of memory. With your current patch, we'd start moving these zero-filled pages to the inode cache and write them out:
first we'd drop the entries from the page tables, then we'd call "writepage()" to write the page to disk.

If, instead, you don't mark the page dirty immediately, but you wait until somebody does a write, or until the VM layer informs you through the "swapout()" function that somebody had a dirty page table entry, what would happen is that (because nobody actually wrote to the page yet) it would just get dropped from the VM space, and writepage() would never be called.

Quoted 4 lines
Quoted 2 lines

Other than that the approach at least looks reasonable. And cleaner than what we currently have.

Only reasonable? :-(

I did not have time to give it a good check, and "reasonable" is the highest praise I'll give without having checked that there aren't any real gotchas.

I certainly think that this is the right way to do things. I haven't had time to check whether it's perfect.

Linus

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/