Hi, There is some confusion about my latest tmpfs fixes. There were three patches which are cummulative against 2.4.4: 1) deadlock fix for write out of mmap regions. (AFAIK this is integrated in the -ac kernels) 2) encapsulate access to shmem_inode_info 3) Do inline symlinks I attach all these patches to this mail in the case that somebody missed one. Greetings Christoph
diff -uNr 2.4.4/include/linux/shmem_fs.h c/include/linux/shmem_fs.h --- 2.4.4/include/linux/shmem_fs.h Sun Apr 29 20:33:00 2001 +++ c/include/linux/shmem_fs.h Sun Apr 29 22:43:56 2001 @@ -19,6 +19,7 @@ struct shmem_inode_info { spinlock_t lock; + struct semaphore sem; unsigned long max_index; swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */ swp_entry_t **i_indirect; /* doubly indirect blocks */ diff -uNr 2.4.4/mm/shmem.c c/mm/shmem.c --- 2.4.4/mm/shmem.c Mon Apr 30 09:45:39 2001 +++ c/mm/shmem.c Tue May 1 15:15:38 2001 @@ -161,6 +161,7 @@ swp_entry_t **base, **ptr, **last; struct shmem_inode_info * info = &inode->u.shmem_i; + down(&info->sem); inode->i_ctime = inode->i_mtime = CURRENT_TIME; spin_lock (&info->lock); index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; @@ -197,6 +198,7 @@ info->swapped -= freed; shmem_recalc_inode(inode); spin_unlock (&info->lock); + up(&info->sem); } static void shmem_delete_inode(struct inode * inode) @@ -281,15 +283,12 @@ * 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 inode * inode, unsigned long idx) +static struct page * shmem_getpage_locked(struct shmem_inode_info *info, struct inode +* inode, unsigned long idx) { struct address_space * mapping = inode->i_mapping; - struct shmem_inode_info *info; struct page * page; swp_entry_t *entry; - info = &inode->u.shmem_i; - repeat: page = find_lock_page(mapping, idx); if (page) @@ -393,6 +392,7 @@ static int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr) { + struct shmem_inode_info *info; struct address_space * mapping = inode->i_mapping; int error; @@ -407,27 +407,28 @@ page_cache_release(*ptr); } - down (&inode->i_sem); - /* retest we may have slept */ + info = &inode->u.shmem_i; + down (&info->sem); + /* retest we may have slept */ + + *ptr = ERR_PTR(-EFAULT); if (inode->i_size < (loff_t) idx * PAGE_CACHE_SIZE) - goto sigbus; - *ptr = shmem_getpage_locked(inode, idx); + goto failed; + + *ptr = shmem_getpage_locked(&inode->u.shmem_i, inode, idx); if (IS_ERR (*ptr)) goto failed; + UnlockPage(*ptr); - up (&inode->i_sem); + up (&info->sem); return 0; failed: - up (&inode->i_sem); + up (&info->sem); error = PTR_ERR(*ptr); - *ptr = NOPAGE_OOM; - if (error != -EFBIG) - *ptr = NOPAGE_SIGBUS; - return error; -sigbus: - up (&inode->i_sem); *ptr = NOPAGE_SIGBUS; - return -EFAULT; + if (error == -ENOMEM) + *ptr = NOPAGE_OOM; + return error; } struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int no_share) @@ -500,6 +501,7 @@ struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev) { struct inode * inode; + struct shmem_inode_info *info; spin_lock (&sb->u.shmem_sb.stat_lock); if (!sb->u.shmem_sb.free_inodes) { @@ -519,7 +521,9 @@ inode->i_rdev = to_kdev_t(dev); inode->i_mapping->a_ops = &shmem_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - spin_lock_init (&inode->u.shmem_i.lock); + info = &inode->u.shmem_i; + spin_lock_init (&info->lock); + sema_init (&info->sem, 1); switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); @@ -549,6 +553,7 @@ shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; + struct shmem_inode_info *info; unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; loff_t pos; struct page *page; @@ -624,7 +629,11 @@ __get_user(dummy, buf+bytes-1); } - page = shmem_getpage_locked(inode, index); + info = &inode->u.shmem_i; + down (&info->sem); + page = shmem_getpage_locked(info, inode, index); + up (&info->sem); + status = PTR_ERR(page); if (IS_ERR(page)) break; @@ -635,7 +644,6 @@ } kaddr = kmap(page); -// can this do a truncated write? cr status = copy_from_user(kaddr+offset, buf, bytes); kunmap(page); if (status) @@ -932,7 +940,7 @@ inode = dentry->d_inode; down(&inode->i_sem); - page = shmem_getpage_locked(inode, 0); + page = shmem_getpage_locked(&inode->u.shmem_i, inode, 0); if (IS_ERR(page)) goto fail; kaddr = kmap(page);
diff -uNr 2.4.4-mmap_write/include/linux/shmem_fs.h 2.4.4-mmap_write-SHMEM_I/include/linux/shmem_fs.h --- 2.4.4-mmap_write/include/linux/shmem_fs.h Tue May 1 20:02:00 2001 +++ 2.4.4-mmap_write-SHMEM_I/include/linux/shmem_fs.h Tue May 1 20:06:10 2001 @@ -18,14 +18,15 @@ } swp_entry_t; struct shmem_inode_info { - spinlock_t lock; - struct semaphore sem; - 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; - int locked; /* into memory */ + spinlock_t lock; + struct semaphore sem; + 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; + int locked; /* into memory */ struct list_head list; + struct inode *inode; }; struct shmem_sb_info { @@ -35,5 +36,7 @@ unsigned long free_inodes; /* How many are left for allocation */ spinlock_t stat_lock; }; + +#define SHMEM_I(inode) (&inode->u.shmem_i) #endif diff -uNr 2.4.4-mmap_write/ipc/shm.c 2.4.4-mmap_write-SHMEM_I/ipc/shm.c --- 2.4.4-mmap_write/ipc/shm.c Wed Apr 11 12:36:47 2001 +++ 2.4.4-mmap_write-SHMEM_I/ipc/shm.c Tue May 1 20:06:10 2001 @@ -348,6 +348,7 @@ static void shm_get_stat (unsigned long *rss, unsigned long *swp) { + struct shmem_inode_info *info; int i; *rss = 0; @@ -361,10 +362,11 @@ if(shp == NULL) continue; inode = shp->shm_file->f_dentry->d_inode; - spin_lock (&inode->u.shmem_i.lock); + info = SHMEM_I(inode); + spin_lock (&info->lock); *rss += inode->i_mapping->nrpages; - *swp += inode->u.shmem_i.swapped; - spin_unlock (&inode->u.shmem_i.lock); + *swp += info->swapped; + spin_unlock (&info->lock); } } diff -uNr 2.4.4-mmap_write/mm/shmem.c 2.4.4-mmap_write-SHMEM_I/mm/shmem.c --- 2.4.4-mmap_write/mm/shmem.c Tue May 1 20:02:00 2001 +++ 2.4.4-mmap_write-SHMEM_I/mm/shmem.c Wed May 2 16:46:00 2001 @@ -73,7 +73,7 @@ unsigned long freed; freed = (inode->i_blocks/BLOCKS_PER_PAGE) - - (inode->i_mapping->nrpages + inode->u.shmem_i.swapped); + (inode->i_mapping->nrpages + SHMEM_I(inode)->swapped); if (freed){ struct shmem_sb_info * info = &inode->i_sb->u.shmem_sb; inode->i_blocks -= freed*BLOCKS_PER_PAGE; @@ -159,7 +159,7 @@ unsigned long index, start; unsigned long freed = 0; swp_entry_t **base, **ptr, **last; - struct shmem_inode_info * info = &inode->u.shmem_i; + struct shmem_inode_info * info = SHMEM_I(inode); down(&info->sem); inode->i_ctime = inode->i_mtime = CURRENT_TIME; @@ -206,7 +206,7 @@ struct shmem_sb_info *info = &inode->i_sb->u.shmem_sb; spin_lock (&shmem_ilock); - list_del (&inode->u.shmem_i.list); + list_del (&SHMEM_I(inode)->list); spin_unlock (&shmem_ilock); inode->i_size = 0; shmem_truncate (inode); @@ -239,7 +239,7 @@ goto out; inode = page->mapping->host; - info = &inode->u.shmem_i; + info = SHMEM_I(inode); swap = __get_swap_page(2); error = -ENOMEM; if (!swap.val) @@ -407,7 +407,7 @@ page_cache_release(*ptr); } - info = &inode->u.shmem_i; + info = SHMEM_I(inode); down (&info->sem); /* retest we may have slept */ @@ -415,7 +415,7 @@ if (inode->i_size < (loff_t) idx * PAGE_CACHE_SIZE) goto failed; - *ptr = shmem_getpage_locked(&inode->u.shmem_i, inode, idx); + *ptr = shmem_getpage_locked(info, inode, idx); if (IS_ERR (*ptr)) goto failed; @@ -462,7 +462,7 @@ 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 shmem_inode_info * info = SHMEM_I(inode); struct page * page; unsigned long idx, size; @@ -521,7 +521,8 @@ inode->i_rdev = to_kdev_t(dev); inode->i_mapping->a_ops = &shmem_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - info = &inode->u.shmem_i; + info = SHMEM_I(inode); + info->inode = inode; spin_lock_init (&info->lock); sema_init (&info->sem, 1); switch (mode & S_IFMT) { @@ -542,7 +543,7 @@ break; } spin_lock (&shmem_ilock); - list_add (&inode->u.shmem_i.list, &shmem_inodes); + list_add (&SHMEM_I(inode)->list, &shmem_inodes); spin_unlock (&shmem_ilock); } return inode; @@ -629,7 +630,7 @@ __get_user(dummy, buf+bytes-1); } - info = &inode->u.shmem_i; + info = SHMEM_I(inode); down (&info->sem); page = shmem_getpage_locked(info, inode, index); up (&info->sem); @@ -658,8 +659,8 @@ 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+1; + if (info->max_index <= index) + info->max_index = index+1; } unlock: @@ -940,7 +941,7 @@ inode = dentry->d_inode; down(&inode->i_sem); - page = shmem_getpage_locked(&inode->u.shmem_i, inode, 0); + page = shmem_getpage_locked(SHMEM_I(inode), inode, 0); if (IS_ERR(page)) goto fail; kaddr = kmap(page); @@ -1220,12 +1221,11 @@ return -1; } -static int shmem_unuse_inode (struct inode *inode, 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 **base, **ptr; unsigned long idx; int offset; - struct shmem_inode_info *info = &inode->u.shmem_i; idx = 0; spin_lock (&info->lock); @@ -1246,7 +1246,7 @@ spin_unlock (&info->lock); return 0; found: - add_to_page_cache(page, inode->i_mapping, offset + idx); + add_to_page_cache(page, info->inode->i_mapping, offset + idx); set_page_dirty(page); SetPageUptodate(page); UnlockPage(page); @@ -1261,13 +1261,13 @@ void shmem_unuse(swp_entry_t entry, struct page *page) { struct list_head *p; - struct inode * inode; + struct shmem_inode_info * info; spin_lock (&shmem_ilock); list_for_each(p, &shmem_inodes) { - inode = list_entry(p, struct inode, u.shmem_i.list); + info = list_entry(p, struct shmem_inode_info, list); - if (shmem_unuse_inode(inode, entry, page)) + if (shmem_unuse_inode(info, entry, page)) break; } spin_unlock (&shmem_ilock);
diff -uNr 2.4.4-mmap_write-SHMEM_I/mm/shmem.c 2.4.4-mmap_write-SHMEM_I-symlink/mm/shmem.c --- 2.4.4-mmap_write-SHMEM_I/mm/shmem.c Fri May 4 21:32:22 2001 +++ 2.4.4-mmap_write-SHMEM_I-symlink/mm/shmem.c Fri May 4 21:37:34 2001 @@ -41,7 +41,6 @@ static struct inode_operations shmem_inode_operations; static struct file_operations shmem_dir_operations; static struct inode_operations shmem_dir_inode_operations; -static struct inode_operations shmem_symlink_inode_operations; static struct vm_operations_struct shmem_vm_ops; LIST_HEAD (shmem_inodes); @@ -205,11 +204,13 @@ { struct shmem_sb_info *info = &inode->i_sb->u.shmem_sb; - 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){ + spin_lock (&shmem_ilock); + list_del (&SHMEM_I(inode)->list); + spin_unlock (&shmem_ilock); + shmem_truncate(inode); + } spin_lock (&info->stat_lock); info->free_inodes++; spin_unlock (&info->stat_lock); @@ -532,6 +533,9 @@ case S_IFREG: inode->i_op = &shmem_inode_operations; inode->i_fop = &shmem_file_operations; + spin_lock (&shmem_ilock); + list_add (&SHMEM_I(inode)->list, &shmem_inodes); + spin_unlock (&shmem_ilock); break; case S_IFDIR: inode->i_nlink++; @@ -539,17 +543,17 @@ inode->i_fop = &shmem_dir_operations; break; case S_IFLNK: - inode->i_op = &shmem_symlink_inode_operations; break; } - spin_lock (&shmem_ilock); - list_add (&SHMEM_I(inode)->list, &shmem_inodes); - spin_unlock (&shmem_ilock); } return inode; } #ifdef CONFIG_TMPFS + +static struct inode_operations shmem_symlink_inode_operations; +static struct inode_operations shmem_symlink_inline_operations; + static ssize_t shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) { @@ -930,33 +934,54 @@ struct inode *inode; struct page *page; char *kaddr; + struct shmem_inode_info * info; error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); if (error) return error; - len = strlen(symname); + len = strlen(symname) + 1; if (len > PAGE_SIZE) return -ENAMETOOLONG; - + inode = dentry->d_inode; - down(&inode->i_sem); - page = shmem_getpage_locked(SHMEM_I(inode), inode, 0); - if (IS_ERR(page)) - goto fail; - kaddr = kmap(page); - memcpy(kaddr, symname, len); - kunmap(page); + info = SHMEM_I(inode); inode->i_size = len; - SetPageDirty(page); - UnlockPage(page); - page_cache_release(page); - up(&inode->i_sem); + if (len <= sizeof(struct shmem_inode_info)) { + /* do it inline */ + memcpy(info, symname, len); + inode->i_op = &shmem_symlink_inline_operations; + } else { + spin_lock (&shmem_ilock); + list_add (&info->list, &shmem_inodes); + spin_unlock (&shmem_ilock); + down(&inode->i_sem); + page = shmem_getpage_locked(info, inode, 0); + if (IS_ERR(page)) { + up(&inode->i_sem); + return PTR_ERR(page); + } + kaddr = kmap(page); + memcpy(kaddr, symname, len); + kunmap(page); + SetPageDirty(page); + UnlockPage(page); + page_cache_release(page); + up(&inode->i_sem); + inode->i_op = &shmem_symlink_inode_operations; + } dir->i_ctime = dir->i_mtime = CURRENT_TIME; return 0; -fail: - up(&inode->i_sem); - return PTR_ERR(page); +} + +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)); +} + +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) @@ -986,6 +1011,17 @@ return res; } +static struct inode_operations shmem_symlink_inline_operations = { + readlink: shmem_readlink_inline, + follow_link: shmem_follow_link_inline, +}; + +static struct inode_operations shmem_symlink_inode_operations = { + truncate: shmem_truncate, + readlink: shmem_readlink, + follow_link: shmem_follow_link, +}; + static int shmem_parse_options(char *options, int *mode, unsigned long * blocks, unsigned long *inodes) { char *this_char, *value; @@ -1118,14 +1154,6 @@ 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 = {