Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a685e089 authored by Al Viro's avatar Al Viro
Browse files

Delay struct net freeing while there's a sysfs instance refering to it



	* new refcount in struct net, controlling actual freeing of the memory
	* new method in kobj_ns_type_operations (->drop_ns())
	* ->current_ns() semantics change - it's supposed to be followed by
corresponding ->drop_ns().  For struct net in case of CONFIG_NET_NS it bumps
the new refcount; net_drop_ns() decrements it and calls net_free() if the
last reference has been dropped.  Method renamed to ->grab_current_ns().
	* old net_free() callers call net_drop_ns() instead.
	* sysfs_exit_ns() is gone, along with a large part of callchain
leading to it; now that the references stored in ->ns[...] stay valid we
do not need to hunt them down and replace them with NULL.  That fixes
problems in sysfs_lookup() and sysfs_readdir(), along with getting rid
of sb->s_instances abuse.

	Note that struct net *shutdown* logics has not changed - net_cleanup()
is called exactly when it used to be called.  The only thing postponed by
having a sysfs instance refering to that struct net is actual freeing of
memory occupied by struct net.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent dde194a6
Loading
Loading
Loading
Loading
+11 −26
Original line number Diff line number Diff line
@@ -95,6 +95,14 @@ static int sysfs_set_super(struct super_block *sb, void *data)
	return error;
}

static void free_sysfs_super_info(struct sysfs_super_info *info)
{
	int type;
	for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
		kobj_ns_drop(type, info->ns[type]);
	kfree(info);
}

static struct dentry *sysfs_mount(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
@@ -108,11 +116,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
		return ERR_PTR(-ENOMEM);

	for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
		info->ns[type] = kobj_ns_current(type);
		info->ns[type] = kobj_ns_grab_current(type);

	sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
	if (IS_ERR(sb) || sb->s_fs_info != info)
		kfree(info);
		free_sysfs_super_info(info);
	if (IS_ERR(sb))
		return ERR_CAST(sb);
	if (!sb->s_root) {
@@ -131,12 +139,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
static void sysfs_kill_sb(struct super_block *sb)
{
	struct sysfs_super_info *info = sysfs_info(sb);

	/* Remove the superblock from fs_supers/s_instances
	 * so we can't find it, before freeing sysfs_super_info.
	 */
	kill_anon_super(sb);
	kfree(info);
	free_sysfs_super_info(info);
}

static struct file_system_type sysfs_fs_type = {
@@ -145,28 +152,6 @@ static struct file_system_type sysfs_fs_type = {
	.kill_sb	= sysfs_kill_sb,
};

void sysfs_exit_ns(enum kobj_ns_type type, const void *ns)
{
	struct super_block *sb;

	mutex_lock(&sysfs_mutex);
	spin_lock(&sb_lock);
	list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
		struct sysfs_super_info *info = sysfs_info(sb);
		/*
		 * If we see a superblock on the fs_supers/s_instances
		 * list the unmount has not completed and sb->s_fs_info
		 * points to a valid struct sysfs_super_info.
		 */
		/* Ignore superblocks with the wrong ns */
		if (info->ns[type] != ns)
			continue;
		info->ns[type] = NULL;
	}
	spin_unlock(&sb_lock);
	mutex_unlock(&sysfs_mutex);
}

int __init sysfs_init(void)
{
	int err = -ENOMEM;
+1 −1
Original line number Diff line number Diff line
@@ -136,7 +136,7 @@ struct sysfs_addrm_cxt {
 * instance).
 */
struct sysfs_super_info {
	const void *ns[KOBJ_NS_TYPES];
	void *ns[KOBJ_NS_TYPES];
};
#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info))
extern struct sysfs_dirent sysfs_root;
+6 −4
Original line number Diff line number Diff line
@@ -32,15 +32,17 @@ enum kobj_ns_type {

/*
 * Callbacks so sysfs can determine namespaces
 *   @current_ns: return calling task's namespace
 *   @grab_current_ns: return a new reference to calling task's namespace
 *   @netlink_ns: return namespace to which a sock belongs (right?)
 *   @initial_ns: return the initial namespace (i.e. init_net_ns)
 *   @drop_ns: drops a reference to namespace
 */
struct kobj_ns_type_operations {
	enum kobj_ns_type type;
	const void *(*current_ns)(void);
	void *(*grab_current_ns)(void);
	const void *(*netlink_ns)(struct sock *sk);
	const void *(*initial_ns)(void);
	void (*drop_ns)(void *);
};

int kobj_ns_type_register(const struct kobj_ns_type_operations *ops);
@@ -48,9 +50,9 @@ int kobj_ns_type_registered(enum kobj_ns_type type);
const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent);
const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj);

const void *kobj_ns_current(enum kobj_ns_type type);
void *kobj_ns_grab_current(enum kobj_ns_type type);
const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk);
const void *kobj_ns_initial(enum kobj_ns_type type);
void kobj_ns_exit(enum kobj_ns_type type, const void *ns);
void kobj_ns_drop(enum kobj_ns_type type, void *ns);

#endif /* _LINUX_KOBJECT_NS_H */
+0 −7
Original line number Diff line number Diff line
@@ -177,9 +177,6 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd);
void sysfs_put(struct sysfs_dirent *sd);

/* Called to clear a ns tag when it is no longer valid */
void sysfs_exit_ns(enum kobj_ns_type type, const void *tag);

int __must_check sysfs_init(void);

#else /* CONFIG_SYSFS */
@@ -338,10 +335,6 @@ static inline void sysfs_put(struct sysfs_dirent *sd)
{
}

static inline void sysfs_exit_ns(int type, const void *tag)
{
}

static inline int __must_check sysfs_init(void)
{
	return 0;
+9 −1
Original line number Diff line number Diff line
@@ -35,9 +35,12 @@ struct netns_ipvs;
#define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS)

struct net {
	atomic_t		count;		/* To decided when the network
	atomic_t		passive;	/* To decided when the network
						 * namespace should be freed.
						 */
	atomic_t		count;		/* To decided when the network
						 *  namespace should be shut down.
						 */
#ifdef NETNS_REFCNT_DEBUG
	atomic_t		use_count;	/* To track references we
						 * destroy on demand
@@ -154,6 +157,9 @@ int net_eq(const struct net *net1, const struct net *net2)
{
	return net1 == net2;
}

extern void net_drop_ns(void *);

#else

static inline struct net *get_net(struct net *net)
@@ -175,6 +181,8 @@ int net_eq(const struct net *net1, const struct net *net2)
{
	return 1;
}

#define net_drop_ns NULL
#endif


Loading