How to abuse RCU to scan the children of a mount?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Paul,

As part of trying to implement a filesystem information querying system call,
I need to be able to return a list of child mounts of a mount.  Children,
however, may be moved with "mount --move", which means that the list can't be
accessed with normal RCU practices.

For reference, I'm dealing with struct mount and mnt_mounts and mnt_child and
struct mount is released via call_rcu().

What does rcu_dereference() guarantee, exactly?  It's just that the next hop
is set up when you follow the pointer, right?

Can I do something like the attached?  The mount 'm' is pinned, but I need to
trawl m->mnt_mounts.  mount_lock is a seqlock that ticks when mount topology
is rearranged.  I *think* it covers ->mnt_mounts.  Whilst trawling in
non-locked mode, I keep an eye on the seq counter and if it changes, the list
may have been altered and I need to get the real lock and restart.

The objects shouldn't disappear or be destroyed, so I think I'm safe.

Thanks,
David
---

int fsinfo_generic_mount_children(struct path *path, struct fsinfo_context *ctx)
{
	struct fsinfo_mount_child record = {};
	struct mount *m, *child;
	int seq = 0;

	m = real_mount(path->mnt);

	rcu_read_lock();
	do {
		ctx->usage = 0;
		read_seqbegin_or_lock(&mount_lock, &seq);
		list_for_each_entry_rcu(child, &m->mnt_mounts, mnt_child) {
			if (need_seqretry(&mount_lock, seq))
				break;
			if (child->mnt_parent != m)
				continue;
			record.mnt_unique_id = child->mnt_unique_id;
			record.mnt_id = child->mnt_id;
			record.notify_sum = 0;
#ifdef CONFIG_SB_NOTIFICATIONS
			record.notify_sum +=
				atomic_read(&child->mnt.mnt_sb->s_change_counter) +
				atomic_read(&child->mnt.mnt_sb->s_notify_counter);
#endif
#ifdef CONFIG_MOUNT_NOTIFICATIONS
			record.notify_sum +=
				atomic_read(&child->mnt_attr_changes) +
				atomic_read(&child->mnt_topology_changes) +
				atomic_read(&child->mnt_subtree_notifications);
#endif
			store_mount_fsinfo(ctx, &record);
		}
	} while (need_seqretry(&mount_lock, seq));
	done_seqretry(&mount_lock, seq);

	rcu_read_unlock();

	/* End the list with a copy of the parameter mount's details so that
	 * userspace can quickly check for changes.
	 */
	record.mnt_unique_id = m->mnt_unique_id;
	record.mnt_id = m->mnt_id;
	record.notify_sum = 0;
#ifdef CONFIG_SB_NOTIFICATIONS
	record.notify_sum +=
		atomic_read(&m->mnt.mnt_sb->s_change_counter) +
		atomic_read(&m->mnt.mnt_sb->s_notify_counter);
#endif
#ifdef CONFIG_MOUNT_NOTIFICATIONS
	record.notify_sum +=
		atomic_read(&m->mnt_attr_changes) +
		atomic_read(&m->mnt_topology_changes) +
		atomic_read(&m->mnt_subtree_notifications);
#endif
	store_mount_fsinfo(ctx, &record);
	return ctx->usage;
}





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux