public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Christoph Rohland <cr@sap.com>
To: linux-kernel <linux-kernel@vger.kernel.org>
Cc: "J . A . Magallon" <jamagallon@able.es>
Subject: [patch] tmpfs for 2.4.1
Date: 01 Feb 2001 22:39:10 +0100	[thread overview]
Message-ID: <m3lmrqrspv.fsf@linux.local> (raw)
In-Reply-To: <20010123205315.A4662@werewolf.able.es>
In-Reply-To: <20010123205315.A4662@werewolf.able.es>

Hi,

here is the latest version of my tmpfs patch against 2.4.1

Have fun
                Christoph

diff -uNr 2.4.1/Documentation/Changes 2.4.1-tmpfs/Documentation/Changes
--- 2.4.1/Documentation/Changes	Tue Jan 30 11:06:59 2001
+++ 2.4.1-tmpfs/Documentation/Changes	Thu Feb  1 22:04:13 2001
@@ -114,20 +114,6 @@
 DevFS is now in the kernel.  See Documentation/filesystems/devfs/* in
 the kernel source tree for all the gory details.
 
-System V shared memory is now implemented via a virtual filesystem.
-You do not have to mount it to use it. SYSV shared memory limits are
-set via /proc/sys/kernel/shm{max,all,mni}.  You should mount the
-filesystem under /dev/shm to be able to use POSIX shared
-memory. Adding the following line to /etc/fstab should take care of
-things:
-
-none		/dev/shm	shm		defaults	0 0
-
-Remember to create the directory that you intend to mount shm on if
-necessary (The entry is automagically created if you use devfs). You
-can set limits for the number of blocks and inodes used by the
-filesystem with the mount options nr_blocks and nr_inodes.
-
 The Logical Volume Manager (LVM) is now in the kernel.  If you want to
 use this, you'll need to install the necessary LVM toolset.
 
diff -uNr 2.4.1/Documentation/Configure.help 2.4.1-tmpfs/Documentation/Configure.help
--- 2.4.1/Documentation/Configure.help	Tue Jan 30 11:06:59 2001
+++ 2.4.1-tmpfs/Documentation/Configure.help	Thu Feb  1 22:06:30 2001
@@ -2739,14 +2739,6 @@
   section 6.4 of the Linux Programmer's Guide, available from
   http://www.linuxdoc.org/docs.html#guide .
 
-  Shared memory is now implemented using a new (minimal) virtual file
-  system. To mount it automatically at system startup just add the
-  following line to your /etc/fstab:
-
-  none	/dev/shm	shm	defaults	0 0
-
-  Saying Y here enlarges your kernel by about 18 KB. Just say Y.
-
 BSD Process Accounting
 CONFIG_BSD_PROCESS_ACCT
   If you say Y here, a user level program will be able to instruct the
@@ -10914,23 +10906,44 @@
 
   If unsure, say N.
    
+Virtual memory file system support
+CONFIG_TMPFS
+  Tmpfs is a file system which keeps all files in virtual memory.
+
+  In contrast to RAM disks, which get allocated a fixed amount of
+  physical RAM, tmpfs grows and shrinks to accommodate the files it
+  contains and is able to swap unneeded pages out to swap space.
+
+  Everything is "virtual" in the sense that no files will be created
+  on your hard drive; if you reboot, everything in tmpfs will be
+  lost.
+  
+  You should mount the filesystem somewhere to be able to use
+  POSIX shared memory. Adding the following line to /etc/fstab should
+  take care of things:
+
+  tmpfs		/dev/shm	tmpfs		defaults	0 0
+
+  Remember to create the directory that you intend to mount tmpfs on
+  if necessary (/dev/shm is automagically created if you use devfs).
+
+  You can set limits for the number of blocks and inodes used by the
+  filesystem with the mount options "size", "nr_blocks" and
+  "nr_inodes". These parameters accept a suffix k, m or g for kilo,
+  mega and giga and can be changed on remount.
+  
+  The initial permissions of the root directory can be set with the
+  mount option "mode".
+
 Simple RAM-based file system support
 CONFIG_RAMFS
   Ramfs is a file system which keeps all files in RAM. It allows
   read and write access.
 
-  In contrast to RAM disks, which get allocated a fixed amount of RAM,
-  ramfs grows and shrinks to accommodate the files it contains.
+  It is more of an programming example than a useable filesystem. If
+  you need a file system which lives in RAM with limit checking use
+  tmpfs.
 
-  Before you can use this RAM-based file system, it has to be mounted,
-  meaning it has to be given a location in the directory hierarchy. If
-  you want to use the location /ramfiles for example, you would have
-  to create that directory first and then mount the file system by
-  saying "mount -t ramfs ramfs /ramfiles" or the equivalent line in
-  /etc/fstab. Everything is "virtual" in the sense that no files will
-  be created on your hard drive; if you reboot, everything in
-  /ramfiles will be lost.
-  
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. The module will be
diff -uNr 2.4.1/arch/i386/kernel/setup.c 2.4.1-tmpfs/arch/i386/kernel/setup.c
--- 2.4.1/arch/i386/kernel/setup.c	Tue Jan 30 11:07:00 2001
+++ 2.4.1-tmpfs/arch/i386/kernel/setup.c	Thu Feb  1 22:02:48 2001
@@ -559,7 +559,7 @@
 				 * blow away any automatically generated
 				 * size
 				 */
-				unsigned long start_at, mem_size;
+				unsigned long long start_at, mem_size;
  
 				if (usermem == 0) {
 					/* first time in: zap the whitelist
diff -uNr 2.4.1/fs/Config.in 2.4.1-tmpfs/fs/Config.in
--- 2.4.1/fs/Config.in	Tue Jan 30 11:07:11 2001
+++ 2.4.1-tmpfs/fs/Config.in	Thu Feb  1 22:06:30 2001
@@ -31,6 +31,7 @@
 	int 'JFFS debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_JFFS_FS_VERBOSE 0
 fi
 tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
 tristate 'Simple RAM-based file system support' CONFIG_RAMFS
 
 tristate 'ISO 9660 CDROM file system support' CONFIG_ISO9660_FS
diff -uNr 2.4.1/include/linux/fs.h 2.4.1-tmpfs/include/linux/fs.h
--- 2.4.1/include/linux/fs.h	Thu Feb  1 22:19:59 2001
+++ 2.4.1-tmpfs/include/linux/fs.h	Thu Feb  1 22:23:28 2001
@@ -1213,6 +1213,7 @@
 	}
 	return inode;
 }
+extern void remove_suid(struct inode *inode);
 
 extern void insert_inode_hash(struct inode *);
 extern void remove_inode_hash(struct inode *);
@@ -1260,6 +1261,7 @@
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
 
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
+extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
 extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t);
diff -uNr 2.4.1/include/linux/kernel.h 2.4.1-tmpfs/include/linux/kernel.h
--- 2.4.1/include/linux/kernel.h	Sun Dec 17 12:54:01 2000
+++ 2.4.1-tmpfs/include/linux/kernel.h	Thu Feb  1 22:02:48 2001
@@ -62,7 +62,7 @@
 extern int vsprintf(char *buf, const char *, va_list);
 extern int get_option(char **str, int *pint);
 extern char *get_options(char *str, int nints, int *ints);
-extern unsigned long memparse(char *ptr, char **retptr);
+extern unsigned long long memparse(char *ptr, char **retptr);
 extern void dev_probe_lock(void);
 extern void dev_probe_unlock(void);
 
diff -uNr 2.4.1/include/linux/mm.h 2.4.1-tmpfs/include/linux/mm.h
--- 2.4.1/include/linux/mm.h	Thu Feb  1 22:09:11 2001
+++ 2.4.1-tmpfs/include/linux/mm.h	Thu Feb  1 22:23:33 2001
@@ -200,8 +200,8 @@
 					smp_mb__before_clear_bit(); \
 					if (!test_and_clear_bit(PG_locked, &(page)->flags)) BUG(); \
 					smp_mb__after_clear_bit(); \
-					if (waitqueue_active(&page->wait)) \
-						wake_up(&page->wait); \
+					if (waitqueue_active(&(page)->wait)) \
+						wake_up(&(page)->wait); \
 				} while (0)
 #define PageError(page)		test_bit(PG_error, &(page)->flags)
 #define SetPageError(page)	set_bit(PG_error, &(page)->flags)
diff -uNr 2.4.1/include/linux/shmem_fs.h 2.4.1-tmpfs/include/linux/shmem_fs.h
--- 2.4.1/include/linux/shmem_fs.h	Tue Jan  2 21:58:11 2001
+++ 2.4.1-tmpfs/include/linux/shmem_fs.h	Thu Feb  1 22:12:49 2001
@@ -19,6 +19,7 @@
 
 struct shmem_inode_info {
 	spinlock_t	lock;
+	unsigned long	max_index;
 	swp_entry_t	i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
 	swp_entry_t   **i_indirect; /* doubly indirect blocks */
 	unsigned long	swapped;
diff -uNr 2.4.1/lib/cmdline.c 2.4.1-tmpfs/lib/cmdline.c
--- 2.4.1/lib/cmdline.c	Mon Aug 28 11:42:45 2000
+++ 2.4.1-tmpfs/lib/cmdline.c	Thu Feb  1 22:02:48 2001
@@ -93,9 +93,9 @@
  *	megabyte, or one gigabyte, respectively.
  */
 
-unsigned long memparse (char *ptr, char **retptr)
+unsigned long long memparse (char *ptr, char **retptr)
 {
-	unsigned long ret = simple_strtoul (ptr, retptr, 0);
+	unsigned long long ret = simple_strtoul (ptr, retptr, 0);
 
 	switch (**retptr) {
 	case 'G':
diff -uNr 2.4.1/mm/filemap.c 2.4.1-tmpfs/mm/filemap.c
--- 2.4.1/mm/filemap.c	Tue Jan 30 11:07:14 2001
+++ 2.4.1-tmpfs/mm/filemap.c	Thu Feb  1 22:22:33 2001
@@ -1209,7 +1209,7 @@
 	UPDATE_ATIME(inode);
 }
 
-static int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
+int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
 {
 	char *kaddr;
 	unsigned long left, count = desc->count;
@@ -2408,7 +2408,7 @@
 	return page;
 }
 
-static inline void remove_suid(struct inode *inode)
+inline void remove_suid(struct inode *inode)
 {
 	unsigned int mode;
 
diff -uNr 2.4.1/mm/shmem.c 2.4.1-tmpfs/mm/shmem.c
--- 2.4.1/mm/shmem.c	Tue Jan 30 11:07:14 2001
+++ 2.4.1-tmpfs/mm/shmem.c	Thu Feb  1 22:15:33 2001
@@ -1,5 +1,5 @@
 /*
- * Resizable simple shmem filesystem for Linux.
+ * Resizable virtual memory filesystem for Linux.
  *
  * Copyright (C) 2000 Linus Torvalds.
  *		 2000 Transmeta Corp.
@@ -9,14 +9,12 @@
  */
 
 /*
- * This shared memory handling is heavily based on the ramfs. It
- * extends the ramfs by the ability to use swap which would makes it a
- * completely usable filesystem.
- *
- * But read and write are not supported (yet)
- *
+ * This virtual memory filesystem is heavily based on the ramfs. It
+ * extends ramfs by the ability to use swap and honor resource limits
+ * which makes it a completely usable filesystem.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
@@ -31,7 +29,8 @@
 
 #include <asm/uaccess.h>
 
-#define SHMEM_MAGIC	0x01021994
+/* This magic number is used in glibc for posix shared memory */
+#define TMPFS_MAGIC	0x01021994
 
 #define ENTRIES_PER_PAGE (PAGE_SIZE/sizeof(unsigned long))
 #define NR_SINGLE (ENTRIES_PER_PAGE + SHMEM_NR_DIRECT)
@@ -42,33 +41,70 @@
 static struct inode_operations shmem_inode_operations;
 static struct file_operations shmem_dir_operations;
 static struct inode_operations shmem_dir_inode_operations;
-static struct vm_operations_struct shmem_shared_vm_ops;
-static struct vm_operations_struct shmem_private_vm_ops;
+static struct inode_operations shmem_symlink_inode_operations;
+static struct vm_operations_struct shmem_vm_ops;
 
 LIST_HEAD (shmem_inodes);
 static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED;
 
+/*
+ * shmem_recalc_inode - recalculate the size of an inode
+ *
+ * @inode: inode to recalc
+ *
+ * We have to calculate the free blocks since the mm can drop pages
+ * behind our back
+ *
+ * But we know that normally
+ * inodes->i_blocks == inode->i_mapping->nrpages + info->swapped
+ *
+ * So the mm freed 
+ * inodes->i_blocks - (inode->i_mapping->nrpages + info->swapped)
+ *
+ * It has to be called with the spinlock held.
+ */
+
+static void shmem_recalc_inode(struct inode * inode)
+{
+	unsigned long freed;
+
+	freed = inode->i_blocks -
+		(inode->i_mapping->nrpages + inode->u.shmem_i.swapped);
+	if (freed){
+		struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb;
+		inode->i_blocks -= freed;
+		spin_lock (&info->stat_lock);
+		info->free_blocks += freed;
+		spin_unlock (&info->stat_lock);
+	}
+}
+
 static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index) 
 {
+	unsigned long offset;
+
 	if (index < SHMEM_NR_DIRECT)
 		return info->i_direct+index;
 
 	index -= SHMEM_NR_DIRECT;
-	if (index >= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
-		return NULL;
+	offset = index % ENTRIES_PER_PAGE;
+	index /= ENTRIES_PER_PAGE;
+
+	if (index >= ENTRIES_PER_PAGE)
+		return ERR_PTR(-EFBIG);
 
 	if (!info->i_indirect) {
 		info->i_indirect = (swp_entry_t **) get_zeroed_page(GFP_USER);
 		if (!info->i_indirect)
-			return NULL;
+			return ERR_PTR(-ENOMEM);
 	}
-	if(!(info->i_indirect[index/ENTRIES_PER_PAGE])) {
-		info->i_indirect[index/ENTRIES_PER_PAGE] = (swp_entry_t *) get_zeroed_page(GFP_USER);
-		if (!info->i_indirect[index/ENTRIES_PER_PAGE])
-			return NULL;
+	if(!(info->i_indirect[index])) {
+		info->i_indirect[index] = (swp_entry_t *) get_zeroed_page(GFP_USER);
+		if (!info->i_indirect[index])
+			return ERR_PTR(-ENOMEM);
 	}
 	
-	return info->i_indirect[index/ENTRIES_PER_PAGE]+index%ENTRIES_PER_PAGE;
+	return info->i_indirect[index]+offset;
 }
 
 static int shmem_free_swp(swp_entry_t *dir, unsigned int count)
@@ -98,7 +134,6 @@
  * @dir:	pointer to swp_entries 
  * @size:	number of entries in dir
  * @start:	offset to start from
- * @inode:	inode for statistics
  * @freed:	counter for freed pages
  *
  * It frees the swap entries from dir+start til dir+size
@@ -108,7 +143,7 @@
 
 static unsigned long 
 shmem_truncate_part (swp_entry_t * dir, unsigned long size, 
-		     unsigned long start, struct inode * inode, unsigned long *freed) {
+		     unsigned long start, unsigned long *freed) {
 	if (start > size)
 		return start - size;
 	if (dir)
@@ -117,56 +152,27 @@
 	return 0;
 }
 
-/*
- * shmem_recalc_inode - recalculate the size of an inode
- *
- * @inode: inode to recalc
- *
- * We have to calculate the free blocks since the mm can drop pages
- * behind our back
- *
- * But we know that normally
- * inodes->i_blocks == inode->i_mapping->nrpages + info->swapped
- *
- * So the mm freed 
- * inodes->i_blocks - (inode->i_mapping->nrpages + info->swapped)
- *
- * It has to be called with the spinlock held.
- */
-
-static void shmem_recalc_inode(struct inode * inode)
-{
-	unsigned long freed;
-
-	freed = inode->i_blocks -
-		(inode->i_mapping->nrpages + inode->u.shmem_i.swapped);
-	if (freed){
-		struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb;
-		inode->i_blocks -= freed;
-		spin_lock (&info->stat_lock);
-		info->free_blocks += freed;
-		spin_unlock (&info->stat_lock);
-	}
-}
-
 static void shmem_truncate (struct inode * inode)
 {
 	int clear_base;
-	unsigned long start;
+	unsigned long index, start;
 	unsigned long freed = 0;
-	swp_entry_t **base, **ptr;
+	swp_entry_t **base, **ptr, **last;
 	struct shmem_inode_info * info = &inode->u.shmem_i;
 
 	spin_lock (&info->lock);
-	start = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (index > info->max_index)
+		goto out;
 
-	start = shmem_truncate_part (info->i_direct, SHMEM_NR_DIRECT, start, inode, &freed);
+	start = shmem_truncate_part (info->i_direct, SHMEM_NR_DIRECT, index, &freed);
 
 	if (!(base = info->i_indirect))
-		goto out;;
+		goto out;
 
 	clear_base = 1;
-	for (ptr = base; ptr < base + ENTRIES_PER_PAGE; ptr++) {
+	last = base + ((info->max_index - SHMEM_NR_DIRECT + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE);
+	for (ptr = base; ptr < last; ptr++) {
 		if (!start) {
 			if (!*ptr)
 				continue;
@@ -176,16 +182,16 @@
 			continue;
 		}
 		clear_base = 0;
-		start = shmem_truncate_part (*ptr, ENTRIES_PER_PAGE, start, inode, &freed);
+		start = shmem_truncate_part (*ptr, ENTRIES_PER_PAGE, start, &freed);
 	}
 
-	if (!clear_base) 
-		goto out;
-
-	free_page ((unsigned long)base);
-	info->i_indirect = 0;
+	if (clear_base) {
+		free_page ((unsigned long)base);
+		info->i_indirect = 0;
+	}
 
 out:
+	info->max_index = index;
 	info->swapped -= freed;
 	shmem_recalc_inode(inode);
 	spin_unlock (&info->lock);
@@ -225,16 +231,16 @@
 
 	spin_lock(&info->lock);
 	shmem_recalc_inode(page->mapping->host);
-	entry = shmem_swp_entry (info, page->index);
-	if (!entry)	/* this had been allocted on page allocation */
+	entry = shmem_swp_entry(info, page->index);
+	if (IS_ERR(entry))	/* this had been allocted on page allocation */
 		BUG();
 	error = -EAGAIN;
 	if (entry->val) {
-                __swap_free(swap, 2);
+		__swap_free(swap, 2);
 		goto out;
-        }
+	}
 
-        *entry = swap;
+	*entry = swap;
 	error = 0;
 	/* Remove the from the page cache */
 	lru_cache_del(page);
@@ -252,43 +258,27 @@
 }
 
 /*
- * shmem_nopage - either get the page from swap or allocate a new one
+ * shmem_getpage_locked - either get the page from swap or allocate a new one
  *
  * If we allocate a new one we do not mark it dirty. That's up to the
  * vm. If we swap it in we mark it dirty since we also free the swap
  * entry since a page cannot live in both the swap and page cache
  */
-struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share)
+static struct page * shmem_getpage_locked(struct inode * inode, unsigned long idx)
 {
-	unsigned long size;
-	struct page * page;
-	unsigned int idx;
-	swp_entry_t *entry;
-	struct inode * inode = vma->vm_file->f_dentry->d_inode;
 	struct address_space * mapping = inode->i_mapping;
 	struct shmem_inode_info *info;
+	struct page * page;
+	swp_entry_t *entry;
 
-	idx = (address - vma->vm_start) >> PAGE_SHIFT;
-	idx += vma->vm_pgoff;
-
-	down (&inode->i_sem);
-	size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-	page = NOPAGE_SIGBUS;
-	if ((idx >= size) && (vma->vm_mm == current->mm))
-		goto out;
-
-	/* retry, we may have slept */
-	page = __find_lock_page(mapping, idx, page_hash (mapping, idx));
+	page = find_lock_page(mapping, idx);;
 	if (page)
-		goto cached_page;
+		return page;
 
 	info = &inode->u.shmem_i;
 	entry = shmem_swp_entry (info, idx);
-	if (!entry)
-		goto oom;
-	spin_lock (&info->lock);
-	shmem_recalc_inode(inode);
-	spin_unlock (&info->lock);
+	if (IS_ERR(entry))
+		return (void *)entry;
 	if (entry->val) {
 		unsigned long flags;
 
@@ -300,15 +290,15 @@
 			page = read_swap_cache(*entry);
 			unlock_kernel();
 			if (!page) 
-				goto oom;
+				return ERR_PTR(-ENOMEM);
 		}
 
 		/* We have to this with page locked to prevent races */
+		lock_page(page);
 		spin_lock (&info->lock);
 		swap_free(*entry);
-		lock_page(page);
-		delete_from_swap_cache_nolock(page);
 		*entry = (swp_entry_t) {0};
+		delete_from_swap_cache_nolock(page);
 		flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced) | (1 << PG_arch_1));
 		page->flags = flags | (1 << PG_dirty);
 		add_to_page_cache_locked(page, mapping, idx);
@@ -323,19 +313,70 @@
 		/* Ok, get a new page */
 		page = page_cache_alloc();
 		if (!page)
-			goto oom;
-		clear_user_highpage(page, address);
+			return ERR_PTR(-ENOMEM);
+		clear_highpage(page);
 		inode->i_blocks++;
 		add_to_page_cache (page, mapping, idx);
 	}
 	/* We have the page */
-	SetPageUptodate (page);
+	SetPageUptodate(page);
 	if (info->locked)
 		page_cache_get(page);
+	return page;
+no_space:
+	spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
+	return ERR_PTR(-ENOSPC);
+}
 
-cached_page:
-	UnlockPage (page);
-	up(&inode->i_sem);
+static int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr)
+{
+	struct address_space * mapping = inode->i_mapping;
+	int error;
+
+	*ptr = NOPAGE_SIGBUS;
+	if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE)
+		return -EFAULT;
+
+	*ptr = __find_get_page(mapping, idx, page_hash(mapping, idx));
+	if (*ptr) {
+		if (Page_Uptodate(*ptr))
+			return 0;
+		page_cache_release(*ptr);
+	}
+
+	down (&inode->i_sem);
+	/* retest we may have slept */
+	if (inode->i_size < (loff_t) idx * PAGE_CACHE_SIZE)
+		goto sigbus;
+	*ptr = shmem_getpage_locked(inode, idx);
+	if (IS_ERR (*ptr))
+		goto failed;
+	UnlockPage(*ptr);
+	up (&inode->i_sem);
+	return 0;
+failed:
+	up (&inode->i_sem);
+	error = PTR_ERR(*ptr);
+	*ptr = NOPAGE_OOM;
+	if (error != -EFBIG)
+		*ptr = NOPAGE_SIGBUS;
+	return error;
+sigbus:
+	*ptr = NOPAGE_SIGBUS;
+	return -EFAULT;
+}
+
+struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share)
+{
+	struct page * page;
+	unsigned int idx;
+	struct inode * inode = vma->vm_file->f_dentry->d_inode;
+
+	idx = (address - vma->vm_start) >> PAGE_SHIFT;
+	idx += vma->vm_pgoff;
+
+	if (shmem_getpage(inode, idx, &page))
+		return page;
 
 	if (no_share) {
 		struct page *new_page = page_cache_alloc();
@@ -351,13 +392,45 @@
 
 	flush_page_to_ram (page);
 	return(page);
-no_space:
-	spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
-oom:
-	page = NOPAGE_OOM;
-out:
+}
+
+void shmem_lock(struct file * file, int lock)
+{
+	struct inode * inode = file->f_dentry->d_inode;
+	struct shmem_inode_info * info = &inode->u.shmem_i;
+	struct page * page;
+	unsigned long idx, size;
+
+	if (info->locked == lock)
+		return;
+	down(&inode->i_sem);
+	info->locked = lock;
+	size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	for (idx = 0; idx < size; idx++) {
+		page = find_lock_page(inode->i_mapping, idx);
+		if (!page)
+			continue;
+		if (!lock) {
+			/* release the extra count and our reference */
+			page_cache_release(page);
+			page_cache_release(page);
+		}
+		UnlockPage(page);
+	}
 	up(&inode->i_sem);
-	return page;
+}
+
+static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct vm_operations_struct * ops;
+	struct inode *inode = file->f_dentry->d_inode;
+
+	ops = &shmem_vm_ops;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	UPDATE_ATIME(inode);
+	vma->vm_ops = ops;
+	return 0;
 }
 
 struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
@@ -396,7 +469,8 @@
 			inode->i_fop = &shmem_dir_operations;
 			break;
 		case S_IFLNK:
-			BUG();
+			inode->i_op = &shmem_symlink_inode_operations;
+			break;
 		}
 		spin_lock (&shmem_ilock);
 		list_add (&inode->u.shmem_i.list, &shmem_inodes);
@@ -405,9 +479,221 @@
 	return inode;
 }
 
+#ifdef CONFIG_TMPFS
+static ssize_t
+shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
+{
+	struct inode	*inode = file->f_dentry->d_inode; 
+	unsigned long	limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+	loff_t		pos;
+	struct page	*page;
+	unsigned long	written;
+	long		status;
+	int		err;
+
+
+	down(&inode->i_sem);
+
+	pos = *ppos;
+	err = -EINVAL;
+	if (pos < 0)
+		goto out;
+
+	err = file->f_error;
+	if (err) {
+		file->f_error = 0;
+		goto out;
+	}
+
+	written = 0;
+
+	if (file->f_flags & O_APPEND)
+		pos = inode->i_size;
+
+	/*
+	 * Check whether we've reached the file size limit.
+	 */
+	err = -EFBIG;
+	if (limit != RLIM_INFINITY) {
+		if (pos >= limit) {
+			send_sig(SIGXFSZ, current, 0);
+			goto out;
+		}
+		if (count > limit - pos) {
+			send_sig(SIGXFSZ, current, 0);
+			count = limit - pos;
+		}
+	}
+
+	status	= 0;
+	if (count) {
+		remove_suid(inode);
+		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	}
+
+	while (count) {
+		unsigned long bytes, index, offset;
+		char *kaddr;
+		int deactivate = 1;
+
+		/*
+		 * Try to find the page in the cache. If it isn't there,
+		 * allocate a free page.
+		 */
+		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+		index = pos >> PAGE_CACHE_SHIFT;
+		bytes = PAGE_CACHE_SIZE - offset;
+		if (bytes > count) {
+			bytes = count;
+			deactivate = 0;
+		}
+
+		/*
+		 * Bring in the user page that we will copy from _first_.
+		 * Otherwise there's a nasty deadlock on copying from the
+		 * same page as we're writing to, without it being marked
+		 * up-to-date.
+		 */
+		{ volatile unsigned char dummy;
+			__get_user(dummy, buf);
+			__get_user(dummy, buf+bytes-1);
+		}
+
+		page = shmem_getpage_locked(inode, index);
+		status = PTR_ERR(page);
+		if (IS_ERR(page))
+			break;
+
+		/* We have exclusive IO access to the page.. */
+		if (!PageLocked(page)) {
+			PAGE_BUG(page);
+		}
+
+		kaddr = kmap(page);
+// can this do a truncated write? cr
+		status = copy_from_user(kaddr+offset, buf, bytes);
+		kunmap(page);
+		if (status)
+			goto fail_write;
+
+		flush_dcache_page(page);
+		if (bytes > 0) {
+			SetPageDirty(page);
+			written += bytes;
+			count -= bytes;
+			pos += bytes;
+			buf += bytes;
+			if (pos > inode->i_size) 
+				inode->i_size = pos;
+			if (inode->u.shmem_i.max_index < index)
+				inode->u.shmem_i.max_index = index;
+
+		}
+unlock:
+		/* Mark it unlocked again and drop the page.. */
+		UnlockPage(page);
+		if (deactivate)
+			deactivate_page(page);
+		page_cache_release(page);
+
+		if (status < 0)
+			break;
+	}
+	*ppos = pos;
+
+	err = written ? written : status;
+out:
+	up(&inode->i_sem);
+	return err;
+fail_write:
+	status = -EFAULT;
+	ClearPageUptodate(page);
+	kunmap(page);
+	goto unlock;
+}
+
+static void do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct address_space *mapping = inode->i_mapping;
+	unsigned long index, offset;
+	int nr = 1;
+
+	index = *ppos >> PAGE_CACHE_SHIFT;
+	offset = *ppos & ~PAGE_CACHE_MASK;
+
+	while (nr && desc->count) {
+		struct page *page;
+		unsigned long end_index, nr;
+
+		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+		if (index > end_index)
+			break;
+		nr = PAGE_CACHE_SIZE;
+		if (index == end_index) {
+			nr = inode->i_size & ~PAGE_CACHE_MASK;
+			if (nr <= offset)
+				break;
+		}
+
+		nr = nr - offset;
+
+		if ((desc->error = shmem_getpage(inode, index, &page)))
+			break;
+
+		if (mapping->i_mmap_shared != NULL)
+			flush_dcache_page(page);
+
+		/*
+		 * Ok, we have the page, and it's up-to-date, so
+		 * now we can copy it to user space...
+		 *
+		 * The actor routine returns how many bytes were actually used..
+		 * NOTE! This may not be the same as how much of a user buffer
+		 * we filled up (we may be padding etc), so we can only update
+		 * "pos" here (the actor routine has to update the user buffer
+		 * pointers and the remaining count).
+		 */
+		nr = file_read_actor(desc, page, offset, nr);
+		offset += nr;
+		index += offset >> PAGE_CACHE_SHIFT;
+		offset &= ~PAGE_CACHE_MASK;
+	
+		page_cache_release(page);
+	}
+
+	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
+	UPDATE_ATIME(inode);
+}
+
+static ssize_t shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+{
+	ssize_t retval;
+
+	retval = -EFAULT;
+	if (access_ok(VERIFY_WRITE, buf, count)) {
+		retval = 0;
+
+		if (count) {
+			read_descriptor_t desc;
+
+			desc.written = 0;
+			desc.count = count;
+			desc.buf = buf;
+			desc.error = 0;
+			do_shmem_file_read(filp, ppos, &desc);
+
+			retval = desc.written;
+			if (!retval)
+				retval = desc.error;
+		}
+	}
+	return retval;
+}
+
 static int shmem_statfs(struct super_block *sb, struct statfs *buf)
 {
-	buf->f_type = SHMEM_MAGIC;
+	buf->f_type = TMPFS_MAGIC;
 	buf->f_bsize = PAGE_CACHE_SIZE;
 	spin_lock (&sb->u.shmem_sb.stat_lock);
 	if (sb->u.shmem_sb.max_blocks != ULONG_MAX || 
@@ -422,32 +708,6 @@
 	return 0;
 }
 
-void shmem_lock(struct file * file, int lock)
-{
-	struct inode * inode = file->f_dentry->d_inode;
-	struct shmem_inode_info * info = &inode->u.shmem_i;
-	struct page * page;
-	unsigned long idx, size;
-
-	if (info->locked == lock)
-		return;
-	down(&inode->i_sem);
-	info->locked = lock;
-	size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-	for (idx = 0; idx < size; idx++) {
-		page = find_lock_page(inode->i_mapping, idx);
-		if (!page)
-			continue;
-		if (!lock) {
-			/* release the extra count and our reference */
-			page_cache_release(page);
-			page_cache_release(page);
-		}
-		UnlockPage(page);
-	}
-	up(&inode->i_sem);
-}
-
 /*
  * Lookup the data. This is trivial - if the dentry didn't already
  * exist, we know it is negative.
@@ -575,19 +835,66 @@
 	return error;
 }
 
-static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
 {
-	struct vm_operations_struct * ops;
-	struct inode *inode = file->f_dentry->d_inode;
+	int error;
+	int len;
+	struct inode *inode;
+	struct page *page;
+	char *kaddr;
 
-	ops = &shmem_private_vm_ops;
-	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
-		ops = &shmem_shared_vm_ops;
-	if (!inode->i_sb || !S_ISREG(inode->i_mode))
-		return -EACCES;
-	UPDATE_ATIME(inode);
-	vma->vm_ops = ops;
+	error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
+	if (error)
+		return error;
+
+	len = strlen(symname);
+	if (len > PAGE_SIZE)
+		return -ENAMETOOLONG;
+		
+	inode = dentry->d_inode;
+	down(&inode->i_sem);
+	page = shmem_getpage_locked(inode, 0);
+	if (IS_ERR(page))
+		goto fail;
+	kaddr = kmap(page);
+	memcpy(kaddr, symname, len);
+	kunmap(page);
+	inode->i_size = len;
+	SetPageDirty(page);
+	UnlockPage(page);
+	page_cache_release(page);
+	up(&inode->i_sem);
 	return 0;
+fail:
+	up(&inode->i_sem);
+	return PTR_ERR(page);
+}
+
+static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	struct page * page;
+	int res = shmem_getpage(dentry->d_inode, 0, &page);
+
+	if (res)
+		return res;
+
+	res = vfs_readlink(dentry,buffer,buflen, kmap(page));
+	kunmap(page);
+	page_cache_release(page);
+	return res;
+}
+
+static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct page * page;
+	int res = shmem_getpage(dentry->d_inode, 0, &page);
+	if (res)
+		return res;
+
+	res = vfs_follow_link(nd, kmap(page));
+	kunmap(page);
+	page_cache_release(page);
+	return res;
 }
 
 static int shmem_parse_options(char *options, int *mode, unsigned long * blocks, unsigned long *inodes)
@@ -600,16 +907,24 @@
 	for ( ; this_char; this_char = strtok(NULL,",")) {
 		if ((value = strchr(this_char,'=')) != NULL)
 			*value++ = 0;
-		if (!strcmp(this_char,"nr_blocks")) {
+		if (!strcmp(this_char,"size")) {
+			unsigned long long size;
 			if (!value || !*value || !blocks)
 				return 1;
-			*blocks = simple_strtoul(value,&value,0);
+			size = memparse(value,&value);
+			if (*value)
+				return 1;
+			*blocks = size >> PAGE_CACHE_SHIFT;
+		} else if (!strcmp(this_char,"nr_blocks")) {
+			if (!value || !*value || !blocks)
+				return 1;
+			*blocks = memparse(value,&value);
 			if (*value)
 				return 1;
 		} else if (!strcmp(this_char,"nr_inodes")) {
 			if (!value || !*value || !inodes)
 				return 1;
-			*inodes = simple_strtoul(value,&value,0);
+			*inodes = memparse(value,&value);
 			if (*value)
 				return 1;
 		} else if (!strcmp(this_char,"mode")) {
@@ -622,10 +937,38 @@
 		else
 			return 1;
 	}
-
 	return 0;
 }
 
+static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
+{
+	int error;
+	unsigned long max_blocks, blocks;
+	unsigned long max_inodes, inodes;
+	struct shmem_sb_info *info = &sb->u.shmem_sb;
+
+	if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
+		return -EINVAL;
+
+	spin_lock(&info->stat_lock);
+	blocks = info->max_blocks - info->free_blocks;
+	inodes = info->max_inodes - info->free_inodes;
+	error = -EINVAL;
+	if (max_blocks < blocks)
+		goto out;
+	if (max_inodes < inodes)
+		goto out;
+	error = 0;
+	info->max_blocks  = max_blocks;
+	info->free_blocks = max_blocks - blocks;
+	info->max_inodes  = max_inodes;
+	info->free_inodes = max_inodes - inodes;
+out:
+	spin_unlock(&info->stat_lock);
+	return error;
+}
+#endif
+
 static struct super_block *shmem_read_super(struct super_block * sb, void * data, int silent)
 {
 	struct inode * inode;
@@ -634,10 +977,12 @@
 	unsigned long inodes = ULONG_MAX;	/* unlimited */
 	int mode   = S_IRWXUGO | S_ISVTX;
 
+#ifdef CONFIG_TMPFS
 	if (shmem_parse_options (data, &mode, &blocks, &inodes)) {
-		printk(KERN_ERR "shmem fs invalid option\n");
+		printk(KERN_ERR "tmpfs invalid option\n");
 		return NULL;
 	}
+#endif
 
 	spin_lock_init (&sb->u.shmem_sb.stat_lock);
 	sb->u.shmem_sb.max_blocks = blocks;
@@ -646,7 +991,7 @@
 	sb->u.shmem_sb.free_inodes = inodes;
 	sb->s_blocksize = PAGE_CACHE_SIZE;
 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
-	sb->s_magic = SHMEM_MAGIC;
+	sb->s_magic = TMPFS_MAGIC;
 	sb->s_op = &shmem_ops;
 	inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
 	if (!inode)
@@ -661,103 +1006,102 @@
 	return sb;
 }
 
-static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
-{
-	int error;
-	unsigned long max_blocks, blocks;
-	unsigned long max_inodes, inodes;
-	struct shmem_sb_info *info = &sb->u.shmem_sb;
-
-	if (shmem_parse_options (data, NULL, &max_blocks, &max_inodes))
-		return -EINVAL;
-
-	spin_lock(&info->stat_lock);
-	blocks = info->max_blocks - info->free_blocks;
-	inodes = info->max_inodes - info->free_inodes;
-	error = -EINVAL;
-	if (max_blocks < blocks)
-		goto out;
-	if (max_inodes < inodes)
-		goto out;
-	error = 0;
-	info->max_blocks  = max_blocks;
-	info->free_blocks = max_blocks - blocks;
-	info->max_inodes  = max_inodes;
-	info->free_inodes = max_inodes - inodes;
-out:
-	spin_unlock(&info->stat_lock);
-	return error;
-}
-
 static struct address_space_operations shmem_aops = {
 	writepage: shmem_writepage
 };
 
 static struct file_operations shmem_file_operations = {
-	mmap:		shmem_mmap
+	mmap:	shmem_mmap,
+#ifdef CONFIG_TMPFS
+	read:	shmem_file_read,
+	write:	shmem_file_write
+#endif
 };
 
 static struct inode_operations shmem_inode_operations = {
 	truncate:	shmem_truncate,
 };
 
+static struct inode_operations shmem_symlink_inode_operations = {
+	truncate:	shmem_truncate,
+#ifdef CONFIG_TMPFS
+	readlink:	shmem_readlink,
+	follow_link:	shmem_follow_link,
+#endif
+};
+
 static struct file_operations shmem_dir_operations = {
 	read:		generic_read_dir,
 	readdir:	dcache_readdir,
 };
 
 static struct inode_operations shmem_dir_inode_operations = {
+#ifdef CONFIG_TMPFS
 	create:		shmem_create,
 	lookup:		shmem_lookup,
 	link:		shmem_link,
 	unlink:		shmem_unlink,
+	symlink:	shmem_symlink,
 	mkdir:		shmem_mkdir,
 	rmdir:		shmem_rmdir,
 	mknod:		shmem_mknod,
 	rename:		shmem_rename,
+#endif
 };
 
 static struct super_operations shmem_ops = {
+#ifdef CONFIG_TMPFS
 	statfs:		shmem_statfs,
 	remount_fs:	shmem_remount_fs,
+#endif
 	delete_inode:	shmem_delete_inode,
 	put_inode:	force_delete,	
 };
 
-static struct vm_operations_struct shmem_private_vm_ops = {
-	nopage:	shmem_nopage,
-};
-
-static struct vm_operations_struct shmem_shared_vm_ops = {
+static struct vm_operations_struct shmem_vm_ops = {
 	nopage:	shmem_nopage,
 };
 
+#ifdef CONFIG_TMPFS
+/* type "shm" will be tagged obsolete in 2.5 */
 static DECLARE_FSTYPE(shmem_fs_type, "shm", shmem_read_super, FS_LITTER);
+static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER);
+#else
+static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER|FS_NOMOUNT);
+#endif
 
 static int __init init_shmem_fs(void)
 {
 	int error;
 	struct vfsmount * res;
 
+	if ((error = register_filesystem(&tmpfs_fs_type))) {
+		printk (KERN_ERR "Could not register tmpfs\n");
+		return error;
+	}
+#ifdef CONFIG_TMPFS
 	if ((error = register_filesystem(&shmem_fs_type))) {
-		printk (KERN_ERR "Could not register shmem fs\n");
+		printk (KERN_ERR "Could not register shm fs\n");
 		return error;
 	}
-
-	res = kern_mount(&shmem_fs_type);
+	devfs_mk_dir (NULL, "shm", NULL);
+#endif
+	res = kern_mount(&tmpfs_fs_type);
 	if (IS_ERR (res)) {
-		printk (KERN_ERR "could not kern_mount shmem fs\n");
-		unregister_filesystem(&shmem_fs_type);
+		printk (KERN_ERR "could not kern_mount tmpfs\n");
+		unregister_filesystem(&tmpfs_fs_type);
 		return PTR_ERR(res);
 	}
 
-	devfs_mk_dir (NULL, "shm", NULL);
 	return 0;
 }
 
 static void __exit exit_shmem_fs(void)
 {
+#ifdef CONFIG_TMPFS
 	unregister_filesystem(&shmem_fs_type);
+#endif
+	unregister_filesystem(&tmpfs_fs_type);
 }
 
 module_init(init_shmem_fs)
@@ -853,7 +1197,7 @@
 	this.name = name;
 	this.len = strlen(name);
 	this.hash = 0; /* will go */
-	root = shmem_fs_type.kern_mnt->mnt_root;
+	root = tmpfs_fs_type.kern_mnt->mnt_root;
 	dentry = d_alloc(root, &this);
 	if (!dentry)
 		goto out;
@@ -870,7 +1214,8 @@
 
 	d_instantiate(dentry, inode);
 	dentry->d_inode->i_size = size;
-	file->f_vfsmnt = mntget(shmem_fs_type.kern_mnt);
+	shmem_truncate(inode);
+	file->f_vfsmnt = mntget(tmpfs_fs_type.kern_mnt);
 	file->f_dentry = dentry;
 	file->f_op = &shmem_file_operations;
 	file->f_mode = FMODE_WRITE | FMODE_READ;
@@ -901,6 +1246,8 @@
 	if (vma->vm_file)
 		fput (vma->vm_file);
 	vma->vm_file = file;
-	vma->vm_ops = &shmem_shared_vm_ops;
+	vma->vm_ops = &shmem_vm_ops;
 	return 0;
 }
+
+EXPORT_SYMBOL(shmem_file_setup);

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

  reply	other threads:[~2001-02-01 21:34 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-01-23 19:53 swapfs for 2.4.1-pre J . A . Magallon
2001-02-01 21:39 ` Christoph Rohland [this message]
2001-02-01 23:50   ` [patch] tmpfs for 2.4.1 H. Peter Anvin
2001-02-02  9:57     ` Christoph Rohland
2001-02-02 20:52       ` J . A . Magallon
2001-02-02 20:55         ` H. Peter Anvin
2001-02-03  0:06           ` J . A . Magallon
2001-02-03 15:02             ` Christoph Rohland
2001-02-03 14:28           ` Christoph Rohland
2001-02-03 20:27             ` H. Peter Anvin
2001-02-03 20:46               ` J . A . Magallon
2001-02-04  9:53                 ` Christoph Rohland
2001-02-04  9:18               ` Christoph Rohland
     [not found]   ` <20010203234550.A507@squish>
     [not found]     ` <m3zog2n6ec.fsf@linux.local>
     [not found]       ` <20010205210540.A10316@squish>
2001-02-06  9:18         ` VM question (ramfs abuse) Christoph Rohland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m3lmrqrspv.fsf@linux.local \
    --to=cr@sap.com \
    --cc=jamagallon@able.es \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox