Commit 69f07ec9 authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds

tmpfs: use kmemdup for short symlinks

But we've not yet removed the old swp_entry_t i_direct[16] from
shmem_inode_info.  That's because it was still being shared with the
inline symlink.  Remove it now (saving 64 or 128 bytes from shmem inode
size), and use kmemdup() for short symlinks, say, those up to 128 bytes.

I wonder why mpol_free_shared_policy() is done in shmem_destroy_inode()
rather than shmem_evict_inode(), where we usually do such freeing? I
guess it doesn't matter, and I'm not into NUMA mpol testing right now.
Signed-off-by: default avatarHugh Dickins <hughd@google.com>
Acked-by: default avatarRik van Riel <riel@redhat.com>
Reviewed-by: default avatarPekka Enberg <penberg@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6922c0c7
...@@ -8,20 +8,15 @@ ...@@ -8,20 +8,15 @@
/* inode in-kernel data */ /* inode in-kernel data */
#define SHMEM_NR_DIRECT 16
#define SHMEM_SYMLINK_INLINE_LEN (SHMEM_NR_DIRECT * sizeof(swp_entry_t))
struct shmem_inode_info { struct shmem_inode_info {
spinlock_t lock; spinlock_t lock;
unsigned long flags; unsigned long flags;
unsigned long alloced; /* data pages alloced to file */ unsigned long alloced; /* data pages alloced to file */
unsigned long swapped; /* subtotal assigned to swap */
struct shared_policy policy; /* NUMA memory alloc policy */
union { union {
swp_entry_t i_direct[SHMEM_NR_DIRECT]; /* first blocks */ unsigned long swapped; /* subtotal assigned to swap */
char inline_symlink[SHMEM_SYMLINK_INLINE_LEN]; char *symlink; /* unswappable short symlink */
}; };
struct shared_policy policy; /* NUMA memory alloc policy */
struct list_head swaplist; /* chain of maybes on swap */ struct list_head swaplist; /* chain of maybes on swap */
struct list_head xattr_list; /* list of shmem_xattr */ struct list_head xattr_list; /* list of shmem_xattr */
struct inode vfs_inode; struct inode vfs_inode;
......
...@@ -73,6 +73,9 @@ static struct vfsmount *shm_mnt; ...@@ -73,6 +73,9 @@ static struct vfsmount *shm_mnt;
/* Pretend that each entry is of this size in directory's i_size */ /* Pretend that each entry is of this size in directory's i_size */
#define BOGO_DIRENT_SIZE 20 #define BOGO_DIRENT_SIZE 20
/* Symlink up to this size is kmalloc'ed instead of using a swappable page */
#define SHORT_SYMLINK_LEN 128
struct shmem_xattr { struct shmem_xattr {
struct list_head list; /* anchored by shmem_inode_info->xattr_list */ struct list_head list; /* anchored by shmem_inode_info->xattr_list */
char *name; /* xattr name */ char *name; /* xattr name */
...@@ -585,7 +588,8 @@ static void shmem_evict_inode(struct inode *inode) ...@@ -585,7 +588,8 @@ static void shmem_evict_inode(struct inode *inode)
list_del_init(&info->swaplist); list_del_init(&info->swaplist);
mutex_unlock(&shmem_swaplist_mutex); mutex_unlock(&shmem_swaplist_mutex);
} }
} } else
kfree(info->symlink);
list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) { list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
kfree(xattr->name); kfree(xattr->name);
...@@ -1173,7 +1177,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode ...@@ -1173,7 +1177,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
#ifdef CONFIG_TMPFS #ifdef CONFIG_TMPFS
static const struct inode_operations shmem_symlink_inode_operations; static const struct inode_operations shmem_symlink_inode_operations;
static const struct inode_operations shmem_symlink_inline_operations; static const struct inode_operations shmem_short_symlink_operations;
static int static int
shmem_write_begin(struct file *file, struct address_space *mapping, shmem_write_begin(struct file *file, struct address_space *mapping,
...@@ -1638,10 +1642,13 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s ...@@ -1638,10 +1642,13 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
info = SHMEM_I(inode); info = SHMEM_I(inode);
inode->i_size = len-1; inode->i_size = len-1;
if (len <= SHMEM_SYMLINK_INLINE_LEN) { if (len <= SHORT_SYMLINK_LEN) {
/* do it inline */ info->symlink = kmemdup(symname, len, GFP_KERNEL);
memcpy(info->inline_symlink, symname, len); if (!info->symlink) {
inode->i_op = &shmem_symlink_inline_operations; iput(inode);
return -ENOMEM;
}
inode->i_op = &shmem_short_symlink_operations;
} else { } else {
error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL); error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
if (error) { if (error) {
...@@ -1664,9 +1671,9 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s ...@@ -1664,9 +1671,9 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
return 0; return 0;
} }
static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd)
{ {
nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink); nd_set_link(nd, SHMEM_I(dentry->d_inode)->symlink);
return NULL; return NULL;
} }
...@@ -1914,9 +1921,9 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) ...@@ -1914,9 +1921,9 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
} }
#endif /* CONFIG_TMPFS_XATTR */ #endif /* CONFIG_TMPFS_XATTR */
static const struct inode_operations shmem_symlink_inline_operations = { static const struct inode_operations shmem_short_symlink_operations = {
.readlink = generic_readlink, .readlink = generic_readlink,
.follow_link = shmem_follow_link_inline, .follow_link = shmem_follow_short_symlink,
#ifdef CONFIG_TMPFS_XATTR #ifdef CONFIG_TMPFS_XATTR
.setxattr = shmem_setxattr, .setxattr = shmem_setxattr,
.getxattr = shmem_getxattr, .getxattr = shmem_getxattr,
...@@ -2259,10 +2266,8 @@ static void shmem_destroy_callback(struct rcu_head *head) ...@@ -2259,10 +2266,8 @@ static void shmem_destroy_callback(struct rcu_head *head)
static void shmem_destroy_inode(struct inode *inode) static void shmem_destroy_inode(struct inode *inode)
{ {
if ((inode->i_mode & S_IFMT) == S_IFREG) { if ((inode->i_mode & S_IFMT) == S_IFREG)
/* only struct inode is valid if it's an inline symlink */
mpol_free_shared_policy(&SHMEM_I(inode)->policy); mpol_free_shared_policy(&SHMEM_I(inode)->policy);
}
call_rcu(&inode->i_rcu, shmem_destroy_callback); call_rcu(&inode->i_rcu, shmem_destroy_callback);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment