Browse Source

ovl: use i_private only as a key

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Miklos Szeredi 8 years ago
parent
commit
25b7713afe
5 changed files with 37 additions and 20 deletions
  1. 2 2
      fs/overlayfs/inode.c
  2. 1 12
      fs/overlayfs/overlayfs.h
  3. 2 0
      fs/overlayfs/ovl_entry.h
  4. 3 0
      fs/overlayfs/super.c
  5. 29 6
      fs/overlayfs/util.c

+ 2 - 2
fs/overlayfs/inode.c

@@ -453,12 +453,12 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
 
 static int ovl_inode_test(struct inode *inode, void *data)
 {
-	return ovl_inode_real(inode, NULL) == data;
+	return inode->i_private == data;
 }
 
 static int ovl_inode_set(struct inode *inode, void *data)
 {
-	inode->i_private = (void *) (((unsigned long) data) | OVL_ISUPPER_MASK);
+	inode->i_private = data;
 	return 0;
 }
 

+ 1 - 12
fs/overlayfs/overlayfs.h

@@ -60,8 +60,6 @@ struct ovl_fh {
 	u8 fid[0];	/* file identifier */
 } __packed;
 
-#define OVL_ISUPPER_MASK 1UL
-
 static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	int err = vfs_rmdir(dir, dentry);
@@ -175,16 +173,6 @@ static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode)
 	return ret;
 }
 
-static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
-{
-	unsigned long x = (unsigned long) READ_ONCE(inode->i_private);
-
-	if (is_upper)
-		*is_upper = x & OVL_ISUPPER_MASK;
-
-	return (struct inode *) (x & ~OVL_ISUPPER_MASK);
-}
-
 /* util.c */
 int ovl_want_write(struct dentry *dentry);
 void ovl_drop_write(struct dentry *dentry);
@@ -201,6 +189,7 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
 struct dentry *ovl_dentry_upper(struct dentry *dentry);
 struct dentry *ovl_dentry_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_real(struct dentry *dentry);
+struct inode *ovl_inode_real(struct inode *inode, bool *is_upper);
 struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
 void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
 bool ovl_dentry_is_opaque(struct dentry *dentry);

+ 2 - 0
fs/overlayfs/ovl_entry.h

@@ -61,6 +61,8 @@ static inline struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
 
 struct ovl_inode {
 	struct inode vfs_inode;
+	struct inode *upper;
+	struct inode *lower;
 };
 
 static inline struct ovl_inode *OVL_I(struct inode *inode)

+ 3 - 0
fs/overlayfs/super.c

@@ -171,6 +171,9 @@ static struct inode *ovl_alloc_inode(struct super_block *sb)
 {
 	struct ovl_inode *oi = kmem_cache_alloc(ovl_inode_cachep, GFP_KERNEL);
 
+	oi->upper = NULL;
+	oi->lower = NULL;
+
 	return &oi->vfs_inode;
 }
 

+ 29 - 6
fs/overlayfs/util.c

@@ -155,6 +155,22 @@ struct dentry *ovl_dentry_real(struct dentry *dentry)
 	return realdentry;
 }
 
+struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
+{
+	struct inode *realinode = lockless_dereference(OVL_I(inode)->upper);
+	bool isup = false;
+
+	if (!realinode)
+		realinode = OVL_I(inode)->lower;
+	else
+		isup = true;
+
+	if (is_upper)
+		*is_upper = isup;
+
+	return realinode;
+}
+
 struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
 {
 	struct ovl_entry *oe = dentry->d_fsdata;
@@ -233,10 +249,11 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
 void ovl_inode_init(struct inode *inode, struct dentry *dentry)
 {
 	struct inode *realinode = d_inode(ovl_dentry_real(dentry));
-	bool is_upper = ovl_dentry_upper(dentry);
 
-	WRITE_ONCE(inode->i_private, (unsigned long) realinode |
-		   (is_upper ? OVL_ISUPPER_MASK : 0));
+	if (ovl_dentry_upper(dentry))
+		OVL_I(inode)->upper = realinode;
+	else
+		OVL_I(inode)->lower = realinode;
 
 	ovl_copyattr(realinode, inode);
 }
@@ -245,10 +262,16 @@ void ovl_inode_update(struct inode *inode, struct inode *upperinode)
 {
 	WARN_ON(!upperinode);
 	WARN_ON(!inode_unhashed(inode));
-	WRITE_ONCE(inode->i_private,
-		   (unsigned long) upperinode | OVL_ISUPPER_MASK);
-	if (!S_ISDIR(upperinode->i_mode))
+	/*
+	 * Make sure upperinode is consistent before making it visible to
+	 * ovl_inode_real();
+	 */
+	smp_wmb();
+	OVL_I(inode)->upper = upperinode;
+	if (!S_ISDIR(upperinode->i_mode)) {
+		inode->i_private = upperinode;
 		__insert_inode_hash(inode, (unsigned long) upperinode);
+	}
 }
 
 void ovl_dentry_version_inc(struct dentry *dentry)