--- /dev/null
+diff -rupN --exclude='ide*' linux-2.4.20.orig/include/linux/mm.h linux-2.4.20/include/linux/mm.h
+--- linux-2.4.20.orig/include/linux/mm.h 2004-02-10 11:43:10.000000000 +0200
++++ linux-2.4.20/include/linux/mm.h 2004-03-01 13:44:45.000000000 +0200
+@@ -468,7 +468,8 @@ extern void clear_page_tables(struct mm_
+ extern int fail_writepage(struct page *);
+ struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused);
+ struct file *shmem_file_setup(char * name, loff_t size);
+-int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr);
++struct page *shmem_getpage_locked(struct inode *inode, unsigned long idx);
++struct page *shmem_getpage_unlocked(struct inode *inode, unsigned long idx);
+ extern void shmem_lock(struct file * file, int lock);
+ extern int shmem_zero_setup(struct vm_area_struct *);
+
+diff -rupN --exclude='ide*' linux-2.4.20.orig/include/linux/shmem_fs.h linux-2.4.20/include/linux/shmem_fs.h
+--- linux-2.4.20.orig/include/linux/shmem_fs.h 2004-02-10 18:39:17.000000000 +0200
++++ linux-2.4.20/include/linux/shmem_fs.h 2004-02-23 12:40:28.000000000 +0200
+@@ -7,6 +7,9 @@
+
+ #define SHMEM_NR_DIRECT 16
+
++#define SHMEM_MOUNT_IOPEN 0x8000 /* Allow access via iopen */
++#define SHMEM_MOUNT_IOPEN_NOPRIV 0x10000 /* Make iopen world-readable */
++
+ /*
+ * A swap entry has to fit into a "unsigned long", as
+ * the entry is hidden in the "index" field of the
+@@ -38,6 +41,9 @@ struct shmem_inode_info {
+ };
+
+ struct shmem_sb_info {
++ struct dentry *iopen;
++ unsigned long options;
++ unsigned long root_ino;
+ unsigned long max_blocks; /* How many blocks are allowed */
+ unsigned long free_blocks; /* How many are left for allocation */
+ unsigned long max_inodes; /* How many inodes are allowed */
+@@ -59,11 +65,9 @@ shmem_xattr_find(struct inode *inode, co
+ extern ssize_t
+ shmem_xattr_set(struct inode *inode, const char *name,
+ const void *value, u16 valuelen, int flags);
+-
+ extern ssize_t
+ shmem_xattr_get(struct inode *inode, const char *name,
+ void *value, size_t valuelen);
+-
+ extern int
+ shmem_xattr_delete(struct inode *inode, struct shmem_xattr *xattr);
+
+diff -rupN --exclude='ide*' linux-2.4.20.orig/mm/shmem.c linux-2.4.20/mm/shmem.c
+--- linux-2.4.20.orig/mm/shmem.c 2004-02-10 18:44:05.000000000 +0200
++++ linux-2.4.20/mm/shmem.c 2004-03-01 14:37:21.000000000 +0200
+@@ -36,29 +36,45 @@
+ #define TMPFS_MAGIC 0x01021994
+
+ #define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))
++
+ #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512)
+
+-#define SHMEM_MAX_INDEX (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * (ENTRIES_PER_PAGE/2) * (ENTRIES_PER_PAGE+1))
++#define SHMEM_MAX_INDEX (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * \
++ (ENTRIES_PER_PAGE/2) * (ENTRIES_PER_PAGE+1))
++
+ #define SHMEM_MAX_BYTES ((unsigned long long)SHMEM_MAX_INDEX << PAGE_CACHE_SHIFT)
+ #define VM_ACCT(size) (((size) + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT)
+
+ /* Pretend that each entry is of this size in directory's i_size */
+-#define BOGO_DIRENT_SIZE 20
+-
++#define BOGO_DIRENT_SIZE (20)
+ #define SHMEM_SB(sb) (&sb->u.shmem_sb)
+
++#define SHMEM_IOPEN_INO 2
++#define SHMEM_IOPEN_NAME_LEN 32
++
++#define ASSERT(cond) \
++do { \
++ if (!(cond)) { \
++ printk (KERN_EMERG \
++ "Assertion failure in %s() at %s:%d: \"%s\"\n", \
++ __FUNCTION__, __FILE__, __LINE__, # cond); \
++ BUG(); \
++ } \
++} while (0)
++
+ static struct super_operations shmem_ops;
++static struct vm_operations_struct shmem_vm_ops;
+ static struct address_space_operations shmem_aops;
+ static struct file_operations shmem_file_operations;
+ static struct inode_operations shmem_inode_operations;
+ static struct inode_operations shmem_dir_inode_operations;
+-static struct vm_operations_struct shmem_vm_ops;
+
+ LIST_HEAD (shmem_inodes);
+ static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED;
+ atomic_t shmem_nrpages = ATOMIC_INIT(0); /* Not used right now */
+
+-static struct page *shmem_getpage_locked(struct shmem_inode_info *, struct inode *, unsigned long);
++struct page *shmem_getpage_locked(struct inode *inode, unsigned long idx);
++struct page *shmem_getpage_unlocked(struct inode *inode, unsigned long idx);
+
+ #ifdef CONFIG_TMPFS
+ static struct inode_operations shmem_symlink_inode_operations;
+@@ -327,7 +343,7 @@ shmem_getxattr(struct dentry *dentry, co
+ * inode attributes list.*/
+ static int
+ shmem_setxattr(struct dentry *dentry, const char *name,
+- void *value, size_t valuelen, int flags)
++ const void *value, size_t valuelen, int flags)
+ {
+ int error;
+ struct inode *inode = dentry->d_inode;
+@@ -404,8 +420,8 @@ shmem_listxattr(struct dentry *dentry, c
+ * @inode: inode to recalc
+ * @swap: additional swap pages freed externally
+ *
+- * We have to calculate the free blocks since the mm can drop pages
+- * behind our back
++ * We have to calculate the free blocks since the mm can drop pages behind our
++ * back
+ *
+ * But we know that normally
+ * inodes->i_blocks/BLOCKS_PER_PAGE ==
+@@ -441,24 +457,23 @@ static void shmem_recalc_inode(struct in
+ * @page: optional page to add to the structure. Has to be preset to
+ * all zeros
+ *
+- * If there is no space allocated yet it will return -ENOMEM when
+- * page == 0 else it will use the page for the needed block.
++ * If there is no space allocated yet it will return -ENOMEM when page == 0 else
++ * it will use the page for the needed block.
+ *
+ * returns -EFBIG if the index is too big.
+ *
+ *
+ * The swap vector is organized the following way:
+ *
+- * There are SHMEM_NR_DIRECT entries directly stored in the
+- * shmem_inode_info structure. So small files do not need an addional
+- * allocation.
+- *
+- * For pages with index > SHMEM_NR_DIRECT there is the pointer
+- * i_indirect which points to a page which holds in the first half
+- * doubly indirect blocks, in the second half triple indirect blocks:
++ * There are SHMEM_NR_DIRECT entries directly stored in the shmem_inode_info
++ * structure. So small files do not need an addional allocation.
++ *
++ * For pages with index > SHMEM_NR_DIRECT there is the pointer i_indirect which
++ * points to a page which holds in the first half doubly indirect blocks, in the
++ * second half triple indirect blocks:
+ *
+- * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the
+- * following layout (for SHMEM_NR_DIRECT == 16):
++ * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the following
++ * layout (for SHMEM_NR_DIRECT == 16):
+ *
+ * i_indirect -> dir --> 16-19
+ * | +-> 20-23
+@@ -473,7 +488,9 @@ static void shmem_recalc_inode(struct in
+ * +-> 48-51
+ * +-> 52-55
+ */
+-static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index, unsigned long page)
++static swp_entry_t *
++shmem_swp_entry (struct shmem_inode_info *info, unsigned long index,
++ unsigned long page)
+ {
+ unsigned long offset;
+ void **dir;
+@@ -520,7 +537,8 @@ static swp_entry_t * shmem_swp_entry (st
+ * @info: info structure for the inode
+ * @index: index of the page to find
+ */
+-static inline swp_entry_t * shmem_alloc_entry (struct shmem_inode_info *info, unsigned long index)
++static inline swp_entry_t *
++shmem_alloc_entry(struct shmem_inode_info *info, unsigned long index)
+ {
+ unsigned long page = 0;
+ swp_entry_t * res;
+@@ -545,7 +563,8 @@ static inline swp_entry_t * shmem_alloc_
+ * @dir: pointer to the directory
+ * @count: number of entries to scan
+ */
+-static int shmem_free_swp(swp_entry_t *dir, unsigned int count)
++static int
++shmem_free_swp(swp_entry_t *dir, unsigned int count)
+ {
+ swp_entry_t *ptr, entry;
+ int freed = 0;
+@@ -573,7 +592,9 @@ static int shmem_free_swp(swp_entry_t *d
+ */
+
+ static inline unsigned long
+-shmem_truncate_direct(swp_entry_t *** dir, unsigned long start, unsigned long len) {
++shmem_truncate_direct(swp_entry_t ***dir, unsigned long start,
++ unsigned long len)
++{
+ swp_entry_t **last, **ptr;
+ unsigned long off, freed = 0;
+
+@@ -639,7 +660,8 @@ shmem_truncate_indirect(struct shmem_ino
+ BUG();
+
+ baseidx = max & ~(ENTRIES_PER_PAGE*ENTRIES_PER_PAGE-1);
+- base = (swp_entry_t ***) info->i_indirect + ENTRIES_PER_PAGE/2 + baseidx/ENTRIES_PER_PAGE/ENTRIES_PER_PAGE ;
++ base = (swp_entry_t ***) info->i_indirect + ENTRIES_PER_PAGE/2 +
++ baseidx/ENTRIES_PER_PAGE/ENTRIES_PER_PAGE ;
+ len = max - baseidx + 1;
+ baseidx += ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2+SHMEM_NR_DIRECT;
+ }
+@@ -654,7 +676,8 @@ shmem_truncate_indirect(struct shmem_ino
+ return shmem_truncate_direct(base, start, len);
+ }
+
+-static void shmem_truncate (struct inode * inode)
++static void
++shmem_truncate(struct inode *inode)
+ {
+ unsigned long index;
+ unsigned long partial;
+@@ -668,16 +691,16 @@ static void shmem_truncate (struct inode
+ partial = inode->i_size & ~PAGE_CACHE_MASK;
+
+ if (partial) {
+- swp_entry_t *entry = shmem_swp_entry(info, index-1, 0);
+ struct page *page;
+- /*
+- * This check is racy: it's faintly possible that page
+- * was assigned to swap during truncate_inode_pages,
+- * and now assigned to file; but better than nothing.
++ swp_entry_t *entry = shmem_swp_entry(info, index - 1, 0);
++
++ /* This check is racy: it's faintly possible that page was
++ * assigned to swap during truncate_inode_pages, and now
++ * assigned to file; but better than nothing.
+ */
+ if (!IS_ERR(entry) && entry->val) {
+ spin_unlock(&info->lock);
+- page = shmem_getpage_locked(info, inode, index-1);
++ page = shmem_getpage_locked(inode, index - 1);
+ if (!IS_ERR(page)) {
+ memclear_highpage_flush(page, partial,
+ PAGE_CACHE_SIZE - partial);
+@@ -697,8 +720,166 @@ static void shmem_truncate (struct inode
+ up(&info->sem);
+ }
+
+-static void shmem_delete_inode(struct inode * inode)
++static struct inode *
++shmem_find_inode(struct super_block *sb, long int ino)
++{
++ struct list_head *p;
++ struct inode *inode = NULL;
++ struct shmem_inode_info *info;
++
++ spin_lock (&shmem_ilock);
++ list_for_each(p, &shmem_inodes) {
++ info = list_entry(p, struct shmem_inode_info, list);
++
++ if (info->inode->i_ino == ino &&
++ info->inode->i_sb == sb)
++ {
++ inode = info->inode;
++ break;
++ }
++ }
++
++ spin_unlock (&shmem_ilock);
++
++ if (inode)
++ igrab(inode);
++
++ return inode;
++}
++
++#define switch_fields(x,y) do { \
++ __typeof__ (x) __tmp = x; \
++ x = y; y = __tmp; } while (0)
++
++static inline void
++switch_names(struct dentry *dentry, struct dentry *target)
++{
++ const unsigned char *old_name, *new_name;
++
++ memcpy(dentry->d_iname, target->d_iname,
++ DNAME_INLINE_LEN);
++
++ old_name = target->d_name.name;
++ new_name = dentry->d_name.name;
++
++ if (old_name == target->d_iname)
++ old_name = dentry->d_iname;
++
++ if (new_name == dentry->d_iname)
++ new_name = target->d_iname;
++
++ target->d_name.name = new_name;
++ dentry->d_name.name = old_name;
++}
++
++static struct dentry *
++shmem_iopen_lookup(struct inode *dir,
++ struct dentry *dentry)
++{
++ struct inode *inode;
++ unsigned long ino;
++ struct list_head *lp;
++ struct dentry *alternate;
++ char buf[SHMEM_IOPEN_NAME_LEN];
++ struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb);
++
++ if (dentry->d_name.len >= SHMEM_IOPEN_NAME_LEN)
++ return ERR_PTR(-ENAMETOOLONG);
++
++ memcpy(buf, dentry->d_name.name, dentry->d_name.len);
++ buf[dentry->d_name.len] = 0;
++
++ if (strcmp(buf, ".") == 0)
++ ino = dir->i_ino;
++ else if (strcmp(buf, "..") == 0)
++ ino = sbinfo->root_ino;
++ else
++ ino = simple_strtoul(buf, 0, 0);
++
++ if (ino < sbinfo->root_ino)
++ return ERR_PTR(-ENOENT);
++
++ if (!(inode = shmem_find_inode(dir->i_sb, ino)))
++ return ERR_PTR(-ENOENT);
++
++ ASSERT(list_empty(&dentry->d_alias));
++ ASSERT(list_empty(&dentry->d_hash));
++
++ /* preferrably return a connected dentry */
++ spin_lock(&dcache_lock);
++ list_for_each(lp, &inode->i_dentry) {
++ alternate = list_entry(lp, struct dentry, d_alias);
++ ASSERT(!(alternate->d_flags & DCACHE_NFSD_DISCONNECTED));
++ }
++
++ if (!list_empty(&inode->i_dentry)) {
++ alternate = list_entry(inode->i_dentry.next,
++ struct dentry, d_alias);
++ dget_locked(alternate);
++ alternate->d_vfs_flags |= DCACHE_REFERENCED;
++ iput(inode);
++ spin_unlock(&dcache_lock);
++ return alternate;
++ }
++ dentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
++
++ /* d_add(), but don't drop dcache_lock before adding dentry to inode */
++ list_add(&dentry->d_alias, &inode->i_dentry);
++ dentry->d_inode = inode;
++
++ __d_rehash(dentry, 0);
++ spin_unlock(&dcache_lock);
++
++ return NULL;
++}
++
++struct dentry *
++shmem_iopen_unalias(struct dentry *dentry, struct inode *inode)
++{
++ struct dentry *tmp, *goal = NULL;
++ struct list_head *lp;
++
++ list_for_each(lp, &inode->i_dentry) {
++ tmp = list_entry(lp, struct dentry, d_alias);
++ if (tmp->d_flags & DCACHE_NFSD_DISCONNECTED) {
++ ASSERT(tmp->d_alias.next == &inode->i_dentry);
++ ASSERT(tmp->d_alias.prev == &inode->i_dentry);
++ goal = tmp;
++ dget_locked(goal);
++ break;
++ }
++ }
++
++ if (!goal)
++ return NULL;
++
++ goal->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
++ list_del_init(&goal->d_hash);
++
++ list_del(&goal->d_child);
++ list_del(&dentry->d_child);
++
++ switch_names(goal, dentry);
++ switch_fields(goal->d_parent, dentry->d_parent);
++ switch_fields(goal->d_name.len, dentry->d_name.len);
++ switch_fields(goal->d_name.hash, dentry->d_name.hash);
++
++ list_add(&goal->d_child, &goal->d_parent->d_subdirs);
++ list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
++ __d_rehash(goal, 0);
++
++ return goal;
++}
++
++static struct inode_operations iopen_inode_operations = {
++ lookup: shmem_iopen_lookup,
++};
++
++static void
++shmem_delete_inode(struct inode *inode)
+ {
++ struct dentry *dentry;
++
+ #ifdef CONFIG_TMPFS_XATTR
+ struct list_head *tmp, *p;
+ struct shmem_xattr *xattr;
+@@ -706,16 +887,30 @@ static void shmem_delete_inode(struct in
+ #endif
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+
+- if (inode->i_op->truncate == shmem_truncate) {
++ if (inode->i_ino != SHMEM_IOPEN_INO) {
++ /* eliminating iopen alias */
++ spin_lock(&dcache_lock);
++ if (!list_empty(&inode->i_dentry)) {
++ dentry = list_entry(inode->i_dentry.next,
++ struct dentry, d_alias);
++ shmem_iopen_unalias(dentry, inode);
++ }
++ spin_unlock(&dcache_lock);
++
+ spin_lock (&shmem_ilock);
+ list_del (&SHMEM_I(inode)->list);
+ spin_unlock (&shmem_ilock);
+- inode->i_size = 0;
+- shmem_truncate (inode);
++
++ if (inode->i_op->truncate == shmem_truncate) {
++ inode->i_size = 0;
++ shmem_truncate (inode);
++ }
++
++ spin_lock (&sbinfo->stat_lock);
++ sbinfo->free_inodes++;
++ spin_unlock (&sbinfo->stat_lock);
+ }
+- spin_lock (&sbinfo->stat_lock);
+- sbinfo->free_inodes++;
+- spin_unlock (&sbinfo->stat_lock);
++
+ #ifdef CONFIG_TMPFS_XATTR
+ list_for_each_safe(p, tmp, &info->xattrs) {
+ xattr = list_entry(p, struct shmem_xattr, list);
+@@ -725,7 +920,8 @@ static void shmem_delete_inode(struct in
+ clear_inode(inode);
+ }
+
+-static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *ptr, swp_entry_t *eptr)
++static inline int
++shmem_find_swp(swp_entry_t entry, swp_entry_t *ptr, swp_entry_t *eptr)
+ {
+ swp_entry_t *test;
+
+@@ -736,7 +932,9 @@ static inline int shmem_find_swp(swp_ent
+ return -1;
+ }
+
+-static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page)
++static int
++shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry,
++ struct page *page)
+ {
+ swp_entry_t *ptr;
+ unsigned long idx;
+@@ -875,9 +1073,11 @@ getswap:
+ * still need to guard against racing with shm_writepage(), which might
+ * be trying to move the page to the swap cache as we run.
+ */
+-static struct page * shmem_getpage_locked(struct shmem_inode_info *info, struct inode * inode, unsigned long idx)
++struct page *
++shmem_getpage_locked(struct inode *inode, unsigned long idx)
+ {
+ struct address_space * mapping = inode->i_mapping;
++ struct shmem_inode_info *info = SHMEM_I(inode);
+ struct shmem_sb_info *sbinfo;
+ struct page * page;
+ swp_entry_t *entry;
+@@ -941,7 +1141,8 @@ repeat:
+ swap_free(*entry);
+ *entry = (swp_entry_t) {0};
+ delete_from_swap_cache(page);
+- flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced) | (1 << PG_arch_1));
++ 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);
+ info->swapped--;
+@@ -985,46 +1186,53 @@ wait_retry:
+ goto repeat;
+ }
+
+-int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr)
++struct page *
++shmem_getpage_unlocked(struct inode *inode, unsigned long idx)
+ {
++ struct page *page;
+ struct shmem_inode_info *info = SHMEM_I(inode);
+- int error;
+
+- down (&info->sem);
+- *ptr = ERR_PTR(-EFAULT);
+- if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE)
++ down(&info->sem);
++ page = ERR_PTR(-EFAULT);
++
++ if (inode->i_size <= (loff_t)idx * PAGE_CACHE_SIZE)
+ goto failed;
+
+- *ptr = shmem_getpage_locked(info, inode, idx);
+- if (IS_ERR (*ptr))
++ page = shmem_getpage_locked(inode, idx);
++
++ if (IS_ERR(page))
+ goto failed;
+
+- UnlockPage(*ptr);
+- up (&info->sem);
+- return 0;
++ UnlockPage(page);
++ up(&info->sem);
++ return page;
+ failed:
+- up (&info->sem);
+- error = PTR_ERR(*ptr);
+- *ptr = NOPAGE_SIGBUS;
+- if (error == -ENOMEM)
+- *ptr = NOPAGE_OOM;
+- return error;
++ up(&info->sem);
++
++ if (PTR_ERR(page) == -ENOMEM)
++ return NOPAGE_OOM;
++
++ return page;
+ }
+
+-struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused)
++struct page *
++shmem_nopage(struct vm_area_struct *vma,
++ unsigned long address, int unused)
+ {
+- struct page * page;
+ unsigned int idx;
++ struct page * page;
+ struct inode * inode = vma->vm_file->f_dentry->d_inode;
+
+- idx = (address - vma->vm_start) >> PAGE_CACHE_SHIFT;
+- idx += vma->vm_pgoff;
++ idx = ((address - vma->vm_start) >> PAGE_CACHE_SHIFT) +
++ vma->vm_pgoff;
+
+- if (shmem_getpage(inode, idx, &page))
++ page = shmem_getpage_unlocked(inode, idx);
++
++ if (IS_ERR(page))
+ return page;
+
+ flush_page_to_ram(page);
+- return(page);
++ return page;
+ }
+
+ void shmem_lock(struct file * file, int lock)
+@@ -1037,7 +1245,8 @@ void shmem_lock(struct file * file, int
+ up(&info->sem);
+ }
+
+-static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
++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;
+@@ -1050,39 +1259,53 @@ static int shmem_mmap(struct file * file
+ return 0;
+ }
+
+-struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
++static void
++shmem_fill_inode(struct inode *inode, int mode, int dev)
+ {
+- struct inode * inode;
+ struct shmem_inode_info *info;
+- struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
++
++ info = SHMEM_I(inode);
++ info->inode = inode;
++ spin_lock_init (&info->lock);
++ sema_init (&info->sem, 1);
+
+- spin_lock (&sbinfo->stat_lock);
+- if (!sbinfo->free_inodes) {
+- spin_unlock (&sbinfo->stat_lock);
+- return NULL;
+- }
+- sbinfo->free_inodes--;
+- spin_unlock (&sbinfo->stat_lock);
++#ifdef CONFIG_TMPFS_XATTR
++ INIT_LIST_HEAD(&info->xattrs);
++ info->xtail = &info->xattrs;
++#endif
+
+- inode = new_inode(sb);
+- if (inode) {
++ inode->i_blocks = 0;
++ inode->i_rdev = NODEV;
++ inode->i_atime = CURRENT_TIME;
++ inode->i_ctime = CURRENT_TIME;
++ inode->i_mtime = CURRENT_TIME;
++ inode->i_blksize = PAGE_CACHE_SIZE;
++
++ /* handling speciall iopen inode. */
++ if (inode->i_ino == SHMEM_IOPEN_INO) {
++ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
++
++ inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
++
++ if (sbinfo->options & SHMEM_MOUNT_IOPEN_NOPRIV)
++ inode->i_mode |= 0777;
++
++ inode->i_uid = 0;
++ inode->i_gid = 0;
++ inode->i_nlink = 1;
++ inode->i_size = 2 * BOGO_DIRENT_SIZE;
++ inode->i_version = 1;
++ inode->i_generation = 0;
++
++ inode->i_op = &iopen_inode_operations;
++ inode->i_fop = &dcache_dir_ops;
++ inode->i_mapping->a_ops = 0;
++ } else {
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+- inode->i_blksize = PAGE_CACHE_SIZE;
+- inode->i_blocks = 0;
+- inode->i_rdev = NODEV;
+ inode->i_mapping->a_ops = &shmem_aops;
+- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+- info = SHMEM_I(inode);
+- info->inode = inode;
+- spin_lock_init (&info->lock);
+- sema_init (&info->sem, 1);
+
+-#ifdef CONFIG_TMPFS_XATTR
+- INIT_LIST_HEAD(&info->xattrs);
+- info->xtail = &info->xattrs;
+-#endif
+ switch (mode & S_IFMT) {
+ default:
+ init_special_inode(inode, mode, dev);
+@@ -1090,9 +1313,6 @@ struct inode *shmem_get_inode(struct sup
+ case S_IFREG:
+ inode->i_op = &shmem_inode_operations;
+ inode->i_fop = &shmem_file_operations;
+- spin_lock (&shmem_ilock);
+- list_add_tail(&info->list, &shmem_inodes);
+- spin_unlock (&shmem_ilock);
+ break;
+ case S_IFDIR:
+ inode->i_nlink++;
+@@ -1104,12 +1324,59 @@ struct inode *shmem_get_inode(struct sup
+ case S_IFLNK:
+ break;
+ }
++
++ spin_lock (&shmem_ilock);
++ list_add_tail(&info->list, &shmem_inodes);
++ spin_unlock (&shmem_ilock);
++ }
++}
++
++struct inode *
++shmem_get_inode(struct super_block *sb,
++ int mode, int dev, int root)
++{
++ struct inode *inode;
++ struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
++
++ spin_lock (&sbinfo->stat_lock);
++ if (!sbinfo->free_inodes) {
++ spin_unlock (&sbinfo->stat_lock);
++ return NULL;
++ }
++ sbinfo->free_inodes--;
++ spin_unlock (&sbinfo->stat_lock);
++
++ if ((inode = new_inode(sb))) {
++ shmem_fill_inode(inode, mode, dev);
++ if (root)
++ sbinfo->root_ino = inode->i_ino;
+ }
++
+ return inode;
+ }
+
+-static int shmem_set_size(struct shmem_sb_info *info,
+- unsigned long max_blocks, unsigned long max_inodes)
++void shmem_read_inode(struct inode *inode)
++{
++ struct shmem_sb_info *sbinfo;
++
++ if (inode->i_ino != SHMEM_IOPEN_INO)
++ return;
++
++ sbinfo = SHMEM_SB(inode->i_sb);
++
++ spin_lock (&sbinfo->stat_lock);
++ if (!sbinfo->free_inodes) {
++ spin_unlock (&sbinfo->stat_lock);
++ return;
++ }
++ sbinfo->free_inodes--;
++ spin_unlock (&sbinfo->stat_lock);
++ shmem_fill_inode(inode, 0, 0);
++}
++
++static int
++shmem_set_size(struct shmem_sb_info *info, unsigned long max_blocks,
++ unsigned long max_inodes)
+ {
+ int error;
+ unsigned long blocks, inodes;
+@@ -1192,7 +1459,6 @@ shmem_file_write(struct file *file,const
+
+ while (count) {
+ unsigned long bytes, index, offset;
+- char *kaddr;
+
+ /*
+ * Try to find the page in the cache. If it isn't there,
+@@ -1201,9 +1467,9 @@ shmem_file_write(struct file *file,const
+ offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+ index = pos >> PAGE_CACHE_SHIFT;
+ bytes = PAGE_CACHE_SIZE - offset;
+- if (bytes > count) {
++
++ if (bytes > count)
+ bytes = count;
+- }
+
+ /*
+ * Bring in the user page that we will copy from _first_.
+@@ -1218,7 +1484,7 @@ shmem_file_write(struct file *file,const
+
+ info = SHMEM_I(inode);
+ down (&info->sem);
+- page = shmem_getpage_locked(info, inode, index);
++ page = shmem_getpage_locked(inode, index);
+ up (&info->sem);
+
+ status = PTR_ERR(page);
+@@ -1226,17 +1492,19 @@ shmem_file_write(struct file *file,const
+ break;
+
+ /* We have exclusive IO access to the page.. */
+- if (!PageLocked(page)) {
++ if (!PageLocked(page))
+ PAGE_BUG(page);
+- }
+
+- kaddr = kmap(page);
+- status = copy_from_user(kaddr+offset, buf, bytes);
++ status = copy_from_user(kmap(page) + offset,
++ buf, bytes);
++
+ kunmap(page);
++
+ if (status)
+ goto fail_write;
+
+ flush_dcache_page(page);
++
+ if (bytes > 0) {
+ SetPageDirty(page);
+ written += bytes;
+@@ -1266,7 +1534,8 @@ fail_write:
+ goto unlock;
+ }
+
+-static void do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc)
++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;
+@@ -1292,15 +1561,18 @@ static void do_shmem_file_read(struct fi
+
+ nr = nr - offset;
+
+- if ((desc->error = shmem_getpage(inode, index, &page)))
++ page = shmem_getpage_unlocked(inode, index);
++
++ if (IS_ERR(page)) {
++ desc->error = PTR_ERR(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...
++ /* 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
+@@ -1309,6 +1581,8 @@ static void do_shmem_file_read(struct fi
+ * pointers and the remaining count).
+ */
+ nr = file_read_actor(desc, page, offset, nr);
++
++ /* updating counters */
+ offset += nr;
+ index += offset >> PAGE_CACHE_SHIFT;
+ offset &= ~PAGE_CACHE_MASK;
+@@ -1320,7 +1594,8 @@ static void do_shmem_file_read(struct fi
+ UPDATE_ATIME(inode);
+ }
+
+-static ssize_t shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
++static ssize_t
++shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+ {
+ ssize_t retval;
+
+@@ -1345,7 +1620,8 @@ static ssize_t shmem_file_read(struct fi
+ return retval;
+ }
+
+-static int shmem_statfs(struct super_block *sb, struct statfs *buf)
++static int
++shmem_statfs(struct super_block *sb, struct statfs *buf)
+ {
+ struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+
+@@ -1361,22 +1637,62 @@ static int shmem_statfs(struct super_blo
+ return 0;
+ }
+
+-/*
+- * Lookup the data. This is trivial - if the dentry didn't already
+- * exist, we know it is negative.
+- */
+-static struct dentry * shmem_lookup(struct inode *dir, struct dentry *dentry)
++static int
++match_dentry(struct dentry *dentry, const char *name)
++{
++ int len = strlen(name);
++
++ if (dentry->d_name.len != len)
++ return 0;
++
++ if (strncmp(dentry->d_name.name, name, len))
++ return 0;
++
++ return 1;
++}
++
++static int
++shmem_iopen_check(struct inode *dir, struct dentry *dentry)
++{
++ struct inode *inode;
++ struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb);
++
++ if (dir->i_ino != sbinfo->root_ino ||
++ !(sbinfo->options & SHMEM_MOUNT_IOPEN) ||
++ !match_dentry(dentry, "__iopen__"))
++ {
++ return 0;
++ }
++
++ if (!(inode = iget(dir->i_sb, SHMEM_IOPEN_INO)))
++ return 0;
++
++ d_add(dentry, inode);
++
++ spin_lock (&sbinfo->stat_lock);
++ sbinfo->iopen = dentry;
++ spin_unlock (&sbinfo->stat_lock);
++
++ dget(dentry);
++ return 1;
++}
++
++static struct dentry *
++shmem_lookup(struct inode *dir, struct dentry *dentry)
+ {
+- d_add(dentry, NULL);
++ if (!shmem_iopen_check(dir, dentry))
++ d_add(dentry, NULL);
++
+ return NULL;
+ }
+
+ /*
+ * File creation. Allocate an inode, and we're done..
+ */
+-static int shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
++static int
++shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
+ {
+- struct inode * inode = shmem_get_inode(dir->i_sb, mode, dev);
++ struct inode *inode = shmem_get_inode(dir->i_sb, mode, dev, 0);
+ int error = -ENOSPC;
+
+ if (inode) {
+@@ -1386,20 +1702,24 @@ static int shmem_mknod(struct inode *dir
+ dget(dentry); /* Extra count - pin the dentry in core */
+ error = 0;
+ }
++
+ return error;
+ }
+
+-static int shmem_mkdir(struct inode * dir, struct dentry * dentry, int mode)
++static int
++shmem_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+ {
+ int error;
+
+ if ((error = shmem_mknod(dir, dentry, mode | S_IFDIR, 0)))
+ return error;
++
+ dir->i_nlink++;
+ return 0;
+ }
+
+-static int shmem_create(struct inode *dir, struct dentry *dentry, int mode)
++static int
++shmem_create(struct inode *dir, struct dentry *dentry, int mode)
+ {
+ return shmem_mknod(dir, dentry, mode | S_IFREG, 0);
+ }
+@@ -1407,7 +1727,8 @@ static int shmem_create(struct inode *di
+ /*
+ * Link a file..
+ */
+-static int shmem_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
++static int
++shmem_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
+ {
+ struct inode *inode = old_dentry->d_inode;
+
+@@ -1429,13 +1750,11 @@ static inline int shmem_positive(struct
+ }
+
+ /*
+- * Check that a directory is empty (this works
+- * for regular files too, they'll just always be
+- * considered empty..).
++ * Check that a directory is empty (this works for regular files too, they'll
++ * just always be considered empty..).
+ *
+- * Note that an empty directory can still have
+- * children, they just all have to be negative..
+- */
++ * Note that an empty directory can still have children, they just all have to
++ * be negative.. */
+ static int shmem_empty(struct dentry *dentry)
+ {
+ struct list_head *list;
+@@ -1456,18 +1775,22 @@ static int shmem_empty(struct dentry *de
+ return 1;
+ }
+
+-static int shmem_unlink(struct inode * dir, struct dentry *dentry)
++static int
++shmem_unlink(struct inode *dir, struct dentry *dentry)
+ {
+ struct inode *inode = dentry->d_inode;
+
+ dir->i_size -= BOGO_DIRENT_SIZE;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ inode->i_nlink--;
+- dput(dentry); /* Undo the count from "create" - this does all the work */
++
++ /* undo the count from "create" - this does all the work. */
++ dput(dentry);
+ return 0;
+ }
+
+-static int shmem_rmdir(struct inode * dir, struct dentry *dentry)
++static int
++shmem_rmdir(struct inode *dir, struct dentry *dentry)
+ {
+ if (!shmem_empty(dentry))
+ return -ENOTEMPTY;
+@@ -1477,12 +1800,13 @@ static int shmem_rmdir(struct inode * di
+ }
+
+ /*
+- * The VFS layer already does all the dentry stuff for rename,
+- * we just have to decrement the usage count for the target if
+- * it exists so that the VFS layer correctly free's it when it
+- * gets overwritten.
++ * The VFS layer already does all the dentry stuff for rename, we just have to
++ * decrement the usage count for the target if it exists so that the VFS layer
++ * correctly free's it when it gets overwritten.
+ */
+-static int shmem_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
++static int
++shmem_rename(struct inode *old_dir, struct dentry *old_dentry,
++ struct inode *new_dir, struct dentry *new_dentry)
+ {
+ struct inode *inode = old_dentry->d_inode;
+ int they_are_dirs = S_ISDIR(inode->i_mode);
+@@ -1507,19 +1831,20 @@ static int shmem_rename(struct inode * o
+ return 0;
+ }
+
+-static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
++static int
++shmem_symlink(struct inode *dir, struct dentry *dentry,
++ const char *symname)
+ {
+ int len;
+- struct inode *inode;
+ struct page *page;
+- char *kaddr;
+- struct shmem_inode_info * info;
++ struct inode *inode;
++ struct shmem_inode_info *info;
+
+ len = strlen(symname) + 1;
+ if (len > PAGE_CACHE_SIZE)
+ return -ENAMETOOLONG;
+
+- inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
++ inode = shmem_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0, 0);
+ if (!inode)
+ return -ENOSPC;
+
+@@ -1531,7 +1856,7 @@ static int shmem_symlink(struct inode *
+ inode->i_op = &shmem_symlink_inline_operations;
+ } else {
+ down(&info->sem);
+- page = shmem_getpage_locked(info, inode, 0);
++ page = shmem_getpage_locked(inode, 0);
+ if (IS_ERR(page)) {
+ up(&info->sem);
+ iput(inode);
+@@ -1541,8 +1866,7 @@ static int shmem_symlink(struct inode *
+ spin_lock (&shmem_ilock);
+ list_add_tail(&info->list, &shmem_inodes);
+ spin_unlock (&shmem_ilock);
+- kaddr = kmap(page);
+- memcpy(kaddr, symname, len);
++ memcpy(kmap(page), symname, len);
+ kunmap(page);
+ SetPageDirty(page);
+ UnlockPage(page);
+@@ -1556,40 +1880,52 @@ static int shmem_symlink(struct inode *
+ return 0;
+ }
+
+-static int shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen)
++static int
++shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen)
+ {
+- return vfs_readlink(dentry,buffer,buflen, (const char *)SHMEM_I(dentry->d_inode));
++ return vfs_readlink(dentry,buffer, buflen,
++ (const char *)SHMEM_I(dentry->d_inode));
+ }
+
+-static int shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
++static int
++shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
+ {
+ return vfs_follow_link(nd, (const char *)SHMEM_I(dentry->d_inode));
+ }
+
+-static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
++static int
++shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
+ {
+- struct page * page;
+- int res = shmem_getpage(dentry->d_inode, 0, &page);
++ int res;
++ struct page *page;
++
++ page = shmem_getpage_unlocked(dentry->d_inode, 0);
+
+- if (res)
+- return res;
++ if (IS_ERR(page))
++ return PTR_ERR(page);
+
+ 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)
++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;
++ int res;
++
++ page = shmem_getpage_unlocked(dentry->d_inode, 0);
++
++ if (IS_ERR(page))
++ return PTR_ERR(page);
+
+ res = vfs_follow_link(nd, kmap(page));
+ kunmap(page);
+ page_cache_release(page);
++
+ return res;
+ }
+
+@@ -1610,7 +1946,10 @@ static struct inode_operations shmem_sym
+ #endif
+ };
+
+-static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long * blocks, unsigned long *inodes)
++static int
++shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid,
++ unsigned long *blocks, unsigned long *inodes,
++ unsigned long *opts)
+ {
+ char *this_char, *value, *rest;
+
+@@ -1620,11 +1959,6 @@ static int shmem_parse_options(char *opt
+ for ( ; this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ *value++ = 0;
+- } else {
+- printk(KERN_ERR
+- "tmpfs: No value for mount option '%s'\n",
+- this_char);
+- return 1;
+ }
+
+ if (!strcmp(this_char,"size")) {
+@@ -1659,6 +1993,17 @@ static int shmem_parse_options(char *opt
+ *gid = simple_strtoul(value,&rest,0);
+ if (*rest)
+ goto bad_val;
++ } else if (!strcmp(this_char, "iopen")) {
++ *opts |= SHMEM_MOUNT_IOPEN;
++ *opts &= ~SHMEM_MOUNT_IOPEN_NOPRIV;
++ }
++ else if (!strcmp(this_char, "noiopen")) {
++ *opts &= ~SHMEM_MOUNT_IOPEN;
++ *opts &= ~SHMEM_MOUNT_IOPEN_NOPRIV;
++ }
++ else if (!strcmp (this_char, "iopen_nopriv")) {
++ *opts |= SHMEM_MOUNT_IOPEN;
++ *opts |= SHMEM_MOUNT_IOPEN_NOPRIV;
+ } else {
+ printk(KERN_ERR "tmpfs: Bad mount option %s\n",
+ this_char);
+@@ -1674,14 +2019,19 @@ bad_val:
+
+ }
+
+-static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
++static int
++shmem_remount_fs(struct super_block *sb, int *flags, char *data)
+ {
+ struct shmem_sb_info *sbinfo = &sb->u.shmem_sb;
+ unsigned long max_blocks = sbinfo->max_blocks;
+ unsigned long max_inodes = sbinfo->max_inodes;
+
+- if (shmem_parse_options (data, NULL, NULL, NULL, &max_blocks, &max_inodes))
++ if (shmem_parse_options (data, NULL, NULL, NULL, &max_blocks,
++ &max_inodes, &sbinfo->options))
++ {
+ return -EINVAL;
++ }
++
+ return shmem_set_size(sbinfo, max_blocks, max_inodes);
+ }
+
+@@ -1691,7 +2041,8 @@ int shmem_sync_file(struct file * file,
+ }
+ #endif
+
+-static struct super_block *shmem_read_super(struct super_block * sb, void * data, int silent)
++static struct super_block *
++shmem_read_super(struct super_block * sb, void * data, int silent)
+ {
+ struct inode * inode;
+ struct dentry * root;
+@@ -1710,11 +2061,15 @@ static struct super_block *shmem_read_su
+ blocks = inodes = si.totalram / 2;
+
+ #ifdef CONFIG_TMPFS
+- if (shmem_parse_options (data, &mode, &uid, &gid, &blocks, &inodes))
++ if (shmem_parse_options (data, &mode, &uid, &gid, &blocks,
++ &inodes, &sbinfo->options))
++ {
+ return NULL;
++ }
+ #endif
+
+ spin_lock_init (&sbinfo->stat_lock);
++ sbinfo->iopen = NULL;
+ sbinfo->max_blocks = blocks;
+ sbinfo->free_blocks = blocks;
+ sbinfo->max_inodes = inodes;
+@@ -1724,7 +2079,7 @@ static struct super_block *shmem_read_su
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = TMPFS_MAGIC;
+ sb->s_op = &shmem_ops;
+- inode = shmem_get_inode(sb, S_IFDIR | mode, 0);
++ inode = shmem_get_inode(sb, S_IFDIR | mode, 0, 1);
+ if (!inode)
+ return NULL;
+
+@@ -1739,7 +2094,19 @@ static struct super_block *shmem_read_su
+ return sb;
+ }
+
++void shmem_put_super(struct super_block *sb)
++{
++ struct dentry *iopen;
++ struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
+
++ spin_lock(&sbinfo->stat_lock);
++ iopen = sbinfo->iopen;
++ sbinfo->iopen = NULL;
++ spin_unlock(&sbinfo->stat_lock);
++
++ if (iopen)
++ dput(iopen);
++}
+
+ static struct address_space_operations shmem_aops = {
+ writepage: shmem_writepage,
+@@ -1790,11 +2157,13 @@ static struct super_operations shmem_ops
+ remount_fs: shmem_remount_fs,
+ #endif
+ delete_inode: shmem_delete_inode,
+- put_inode: force_delete,
++ read_inode: shmem_read_inode,
++ put_inode: force_delete,
++ put_super: shmem_put_super,
+ };
+
+ static struct vm_operations_struct shmem_vm_ops = {
+- nopage: shmem_nopage,
++ nopage: shmem_nopage,
+ };
+
+ #ifdef CONFIG_TMPFS
+@@ -1885,7 +2254,7 @@ struct file *shmem_file_setup(char * nam
+ goto put_dentry;
+
+ error = -ENOSPC;
+- inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0);
++ inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0, 0);
+ if (!inode)
+ goto close_file;
+
+@@ -1921,15 +2290,17 @@ int shmem_zero_setup(struct vm_area_stru
+
+ if (vma->vm_file)
+ fput (vma->vm_file);
++
+ vma->vm_file = file;
+ vma->vm_ops = &shmem_vm_ops;
+ return 0;
+ }
+
+ EXPORT_SYMBOL(shmem_file_setup);
+-EXPORT_SYMBOL(shmem_getpage);
+ EXPORT_SYMBOL(shmem_xattr_find);
+ EXPORT_SYMBOL(shmem_xattr_set);
+ EXPORT_SYMBOL(shmem_xattr_get);
+ EXPORT_SYMBOL(shmem_xattr_delete);
+ EXPORT_SYMBOL(shmem_xattr_remove);
++EXPORT_SYMBOL(shmem_getpage_locked);
++EXPORT_SYMBOL(shmem_getpage_unlocked);