Commit 7cd122b5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull persistent dentry infrastructure and conversion from Al Viro:
 "Some filesystems use a kinda-sorta controlled dentry refcount leak to
  pin dentries of created objects in dcache (and undo it when removing
  those). A reference is grabbed and not released, but it's not actually
  _stored_ anywhere.

  That works, but it's hard to follow and verify; among other things, we
  have no way to tell _which_ of the increments is intended to be an
  unpaired one. Worse, on removal we need to decide whether the
  reference had already been dropped, which can be non-trivial if that
  removal is on umount and we need to figure out if this dentry is
  pinned due to e.g. unlink() not done. Usually that is handled by using
  kill_litter_super() as ->kill_sb(), but there are open-coded special
  cases of the same (consider e.g. /proc/self).

  Things get simpler if we introduce a new dentry flag
  (DCACHE_PERSISTENT) marking those "leaked" dentries. Having it set
  claims responsibility for +1 in refcount.

  The end result this series is aiming for:

   - get these unbalanced dget() and dput() replaced with new primitives
     that would, in addition to adjusting refcount, set and clear
     persistency flag.

   - instead of having kill_litter_super() mess with removing the
     remaining "leaked" references (e.g. for all tmpfs files that hadn't
     been removed prior to umount), have the regular
     shrink_dcache_for_umount() strip DCACHE_PERSISTENT of all dentries,
     dropping the corresponding reference if it had been set. After that
     kill_litter_super() becomes an equivalent of kill_anon_super().

  Doing that in a single step is not feasible - it would affect too many
  places in too many filesystems. It has to be split into a series.

  This work has really started early in 2024; quite a few preliminary
  pieces have already gone into mainline. This chunk is finally getting
  to the meat of that stuff - infrastructure and most of the conversions
  to it.

  Some pieces are still sitting in the local branches, but the bulk of
  that stuff is here"

* tag 'pull-persistency' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (54 commits)
  d_make_discardable(): warn if given a non-persistent dentry
  kill securityfs_recursive_remove()
  convert securityfs
  get rid of kill_litter_super()
  convert rust_binderfs
  convert nfsctl
  convert rpc_pipefs
  convert hypfs
  hypfs: swich hypfs_create_u64() to returning int
  hypfs: switch hypfs_create_str() to returning int
  hypfs: don't pin dentries twice
  convert gadgetfs
  gadgetfs: switch to simple_remove_by_name()
  convert functionfs
  functionfs: switch to simple_remove_by_name()
  functionfs: fix the open/removal races
  functionfs: need to cancel ->reset_work in ->kill_sb()
  functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
  functionfs: don't abuse ffs_data_closed() on fs shutdown
  convert selinuxfs
  ...
parents 7203ca41 eb028c33
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1327,3 +1327,10 @@ When vfs_mkdir() returns an error, and so both dputs() the original
dentry and doesn't provide a replacement, it also unlocks the parent.
Consequently the return value from vfs_mkdir() can be passed to
end_creating() and the parent will be unlocked precisely when necessary.

---

**mandatory**

kill_litter_super() is gone; convert to DCACHE_PERSISTENT use (as all
in-tree filesystems have done).
+7 −10
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ spufs_new_file(struct super_block *sb, struct dentry *dentry,
	inode->i_fop = fops;
	inode->i_size = size;
	inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
	d_add(dentry, inode);
	d_make_persistent(dentry, inode);
out:
	return ret;
}
@@ -163,10 +163,9 @@ static int spufs_fill_dir(struct dentry *dir,
			return -ENOMEM;
		ret = spufs_new_file(dir->d_sb, dentry, files->ops,
					files->mode & mode, files->size, ctx);
		if (ret) {
		dput(dentry);
		if (ret)
			return ret;
		}
		files++;
	}
	return 0;
@@ -241,11 +240,10 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,

	inode_lock(inode);

	dget(dentry);
	inc_nlink(dir);
	inc_nlink(inode);

	d_instantiate(dentry, inode);
	d_make_persistent(dentry, inode);

	if (flags & SPU_CREATE_NOSCHED)
		ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
@@ -468,10 +466,9 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
	inode->i_op = &simple_dir_inode_operations;
	inode->i_fop = &simple_dir_operations;

	d_instantiate(dentry, inode);
	dget(dentry);
	inc_nlink(dir);
	inc_nlink(d_inode(dentry));
	inc_nlink(inode);
	d_make_persistent(dentry, inode);
	return ret;

out_iput:
@@ -758,7 +755,7 @@ static struct file_system_type spufs_type = {
	.name = "spufs",
	.init_fs_context = spufs_init_fs_context,
	.parameters	= spufs_fs_parameters,
	.kill_sb = kill_litter_super,
	.kill_sb = kill_anon_super,
};
MODULE_ALIAS_FS("spufs");

+2 −4
Original line number Diff line number Diff line
@@ -22,11 +22,9 @@

extern struct dentry *hypfs_mkdir(struct dentry *parent, const char *name);

extern struct dentry *hypfs_create_u64(struct dentry *dir, const char *name,
				       __u64 value);
extern int hypfs_create_u64(struct dentry *dir, const char *name, __u64 value);

extern struct dentry *hypfs_create_str(struct dentry *dir, const char *name,
				       char *string);
extern int hypfs_create_str(struct dentry *dir, const char *name, char *string);

/* LPAR Hypervisor */
extern int hypfs_diag_init(void);
+21 −39
Original line number Diff line number Diff line
@@ -203,7 +203,7 @@ static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
{
	struct dentry *cpu_dir;
	char buffer[TMP_SIZE];
	void *rc;
	int rc;

	snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_get_info_type(),
							    cpu_info));
@@ -213,22 +213,21 @@ static int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info)
	rc = hypfs_create_u64(cpu_dir, "mgmtime",
			      cpu_info__acc_time(diag204_get_info_type(), cpu_info) -
			      cpu_info__lp_time(diag204_get_info_type(), cpu_info));
	if (IS_ERR(rc))
		return PTR_ERR(rc);
	if (rc)
		return rc;
	rc = hypfs_create_u64(cpu_dir, "cputime",
			      cpu_info__lp_time(diag204_get_info_type(), cpu_info));
	if (IS_ERR(rc))
		return PTR_ERR(rc);
	if (rc)
		return rc;
	if (diag204_get_info_type() == DIAG204_INFO_EXT) {
		rc = hypfs_create_u64(cpu_dir, "onlinetime",
				      cpu_info__online_time(diag204_get_info_type(),
							    cpu_info));
		if (IS_ERR(rc))
			return PTR_ERR(rc);
		if (rc)
			return rc;
	}
	diag224_idx2name(cpu_info__ctidx(diag204_get_info_type(), cpu_info), buffer);
	rc = hypfs_create_str(cpu_dir, "type", buffer);
	return PTR_ERR_OR_ZERO(rc);
	return hypfs_create_str(cpu_dir, "type", buffer);
}

static void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr)
@@ -263,7 +262,7 @@ static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
{
	struct dentry *cpu_dir;
	char buffer[TMP_SIZE];
	void *rc;
	int rc;

	snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_get_info_type(),
							    cpu_info));
@@ -272,11 +271,10 @@ static int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info)
		return PTR_ERR(cpu_dir);
	rc = hypfs_create_u64(cpu_dir, "mgmtime",
			      phys_cpu__mgm_time(diag204_get_info_type(), cpu_info));
	if (IS_ERR(rc))
		return PTR_ERR(rc);
	if (rc)
		return rc;
	diag224_idx2name(phys_cpu__ctidx(diag204_get_info_type(), cpu_info), buffer);
	rc = hypfs_create_str(cpu_dir, "type", buffer);
	return PTR_ERR_OR_ZERO(rc);
	return hypfs_create_str(cpu_dir, "type", buffer);
}

static void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr)
@@ -315,41 +313,25 @@ int hypfs_diag_create_files(struct dentry *root)
		return rc;

	systems_dir = hypfs_mkdir(root, "systems");
	if (IS_ERR(systems_dir)) {
		rc = PTR_ERR(systems_dir);
		goto err_out;
	}
	if (IS_ERR(systems_dir))
		return PTR_ERR(systems_dir);
	time_hdr = (struct x_info_blk_hdr *)buffer;
	part_hdr = time_hdr + info_blk_hdr__size(diag204_get_info_type());
	for (i = 0; i < info_blk_hdr__npar(diag204_get_info_type(), time_hdr); i++) {
		part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr);
		if (IS_ERR(part_hdr)) {
			rc = PTR_ERR(part_hdr);
			goto err_out;
		}
		if (IS_ERR(part_hdr))
			return PTR_ERR(part_hdr);
	}
	if (info_blk_hdr__flags(diag204_get_info_type(), time_hdr) &
	    DIAG204_LPAR_PHYS_FLG) {
		ptr = hypfs_create_phys_files(root, part_hdr);
		if (IS_ERR(ptr)) {
			rc = PTR_ERR(ptr);
			goto err_out;
		}
		if (IS_ERR(ptr))
			return PTR_ERR(ptr);
	}
	hyp_dir = hypfs_mkdir(root, "hyp");
	if (IS_ERR(hyp_dir)) {
		rc = PTR_ERR(hyp_dir);
		goto err_out;
	}
	ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
	if (IS_ERR(ptr)) {
		rc = PTR_ERR(ptr);
		goto err_out;
	}
	rc = 0;

err_out:
	return rc;
	if (IS_ERR(hyp_dir))
		return PTR_ERR(hyp_dir);
	return hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor");
}

/* Diagnose 224 functions */
+8 −13
Original line number Diff line number Diff line
@@ -19,10 +19,9 @@

#define ATTRIBUTE(dir, name, member) \
do { \
	void *rc; \
	rc = hypfs_create_u64(dir, name, member); \
	if (IS_ERR(rc)) \
		return PTR_ERR(rc); \
	int rc = hypfs_create_u64(dir, name, member); \
	if (rc) \
		return rc; \
} while (0)

static int hypfs_vm_create_guest(struct dentry *systems_dir,
@@ -85,7 +84,7 @@ static int hypfs_vm_create_guest(struct dentry *systems_dir,

int hypfs_vm_create_files(struct dentry *root)
{
	struct dentry *dir, *file;
	struct dentry *dir;
	struct diag2fc_data *data;
	unsigned int count = 0;
	int rc, i;
@@ -100,11 +99,9 @@ int hypfs_vm_create_files(struct dentry *root)
		rc = PTR_ERR(dir);
		goto failed;
	}
	file = hypfs_create_str(dir, "type", "z/VM Hypervisor");
	if (IS_ERR(file)) {
		rc = PTR_ERR(file);
	rc = hypfs_create_str(dir, "type", "z/VM Hypervisor");
	if (rc)
		goto failed;
	}

	/* physical cpus */
	dir = hypfs_mkdir(root, "cpus");
@@ -112,11 +109,9 @@ int hypfs_vm_create_files(struct dentry *root)
		rc = PTR_ERR(dir);
		goto failed;
	}
	file = hypfs_create_u64(dir, "count", data->lcpus);
	if (IS_ERR(file)) {
		rc = PTR_ERR(file);
	rc = hypfs_create_u64(dir, "count", data->lcpus);
	if (rc)
		goto failed;
	}

	/* guests */
	dir = hypfs_mkdir(root, "systems");
Loading