Re: [RFC] Hack to use mkdir/rmdir in debugfs

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

 



On Thu, 2013-01-24 at 17:39 -0500, Valdis.Kletnieks@xxxxxx wrote:

> > +static int instance_rmdir(struct inode *inode, struct dentry *dentry)
> > +{
> > +	struct dentry *parent;
> > +	int ret;
> > +
> > +	/* Paranoid: Make sure the parent is the "instances" directory */
> > +	parent = hlist_entry(inode->i_dentry.first, struct dentry, d_alias);
> > +	if (WARN_ON_ONCE(parent != trace_instance_dir))
> > +		return -ENOENT;
> > +
> > +	/* The caller did a dget() on dentry */
> > +	mutex_unlock(&dentry->d_inode->i_mutex);
> > +
> > +	/*
> > +	 * The inode mutex is locked, but debugfs_create_dir() will also
> > +	 * take the mutex. As the instances directory can not be destroyed
> > +	 * or changed in any other way, it is safe to unlock it, and
> > +	 * let the dentry try. If two users try to make the same dir at
> > +	 * the same time, then the instance_delete() will determine the
> > +	 * winner.
> > +	 */
> > +	mutex_unlock(&inode->i_mutex);
> 
> Looks like the rmdir comment was slice-n-miced from the mkdir one?
> 
> What exactly *are* the race condition rules here, especially for mkdir
> racing against an rmdir?

Note, after the i_mutex is released, new mutexes are grabbed to test if
we can create or delete a file:

static int new_instance_create(const char *name)
{
        enum ring_buffer_flags rb_flags;
        struct trace_array *tr;
        int ret;
        int i;

        mutex_lock(&trace_types_lock);

        ret = -EEXIST;
        list_for_each_entry(tr, &ftrace_trace_arrays, list) {
                if (tr->name && strcmp(tr->name, name) == 0)
                        goto out_unlock;
        }

        ret = -ENOMEM;
        tr = kzalloc(sizeof(*tr), GFP_KERNEL);
        if (!tr)
                goto out_unlock;

        tr->name = kstrdup(name, GFP_KERNEL);
        if (!tr->name)
                goto out_free_tr;

        raw_spin_lock_init(&tr->start_lock);

        tr->current_trace = &nop_trace;

        INIT_LIST_HEAD(&tr->systems);
        INIT_LIST_HEAD(&tr->events);

        rb_flags = trace_flags & TRACE_ITER_OVERWRITE ? RB_FL_OVERWRITE : 0;

        tr->buffer = ring_buffer_alloc(trace_buf_size, rb_flags);
        if (!tr->buffer)
                goto out_free_tr;

        tr->data = alloc_percpu(struct trace_array_cpu);
        if (!tr->data)
                goto out_free_tr;

        for_each_tracing_cpu(i) {
                memset(per_cpu_ptr(tr->data, i), 0, sizeof(struct trace_array_cpu));
                per_cpu_ptr(tr->data, i)->trace_cpu.cpu = i;
                per_cpu_ptr(tr->data, i)->trace_cpu.tr = tr;
        }

        /* Holder for file callbacks */
        tr->trace_cpu.cpu = RING_BUFFER_ALL_CPUS;
        tr->trace_cpu.tr = tr;

        tr->dir = debugfs_create_dir(name, trace_instance_dir);
        if (!tr->dir)
                goto out_free_tr;

        ret = event_trace_add_tracer(tr->dir, tr);
        if (ret)
                goto out_free_tr;

        init_tracer_debugfs(tr, tr->dir);

        list_add(&tr->list, &ftrace_trace_arrays);

        mutex_unlock(&trace_types_lock);

        return 0;

 out_free_tr:
        if (tr->buffer)
                ring_buffer_free(tr->buffer);
        kfree(tr->name);
        kfree(tr);

 out_unlock:
        mutex_unlock(&trace_types_lock);

        return ret;

}

static int instance_delete(const char *name)
{
        struct trace_array *tr;
        int found = 0;
        int ret;

        mutex_lock(&trace_types_lock);

        ret = -ENODEV;
        list_for_each_entry(tr, &ftrace_trace_arrays, list) {
                if (tr->name && strcmp(tr->name, name) == 0) {
                        found = 1;
                        break;
                }
        }
        if (!found)
                goto out_unlock;

        list_del(&tr->list);

        event_trace_del_tracer(tr);
        debugfs_remove_recursive(tr->dir);
        free_percpu(tr->data);
        ring_buffer_free(tr->buffer);

        kfree(tr->name);
        kfree(tr);

        ret = 0;

 out_unlock:
        mutex_unlock(&trace_types_lock);

        return ret;
}


The trace_types_lock protects the ftrace_trace_arrays, which holds the
descriptors for the entities that can be created or removed. The
debugfs_remove_recursive(), and debugfs_create_dir() etc, are children
of the inode i_mutex that we released. Basically, the trace_types_lock
takes over for creating and destroying directories. It's the same type
of protection I had when I had the "new" and "free" files that would
create the directory when echoing in "new" and delete one when echoing
in "free". There's really no difference. I'm only using the mkdir/rmdir
interface to trigger the exact same interface I had before. They both
have the same locking.

There is no rename or any other manipulation here. Even the parent
directory can't change (no way to delete it).

As for races, I've been running this little script to test it out, and
it works fine:

-- Steve

----
#!/bin/bash

if [ ! -d /debug/tracing/instances ]; then
	echo "No instances directory"
	exit 0
fi

cd /debug/tracing/instances

instance_slam() {
	while :; do
		mkdir x
		mkdir y
		mkdir z
		rmdir x
		rmdir y
		rmdir z
	done 2>/dev/null
}

instance_slam &
x=`jobs -l`
p1=`echo $x | cut -d' ' -f2`
echo $p1

instance_slam &
x=`jobs -l | tail -1`
p2=`echo $x | cut -d' ' -f2`
echo $p2

instance_slam &
x=`jobs -l | tail -1`
p3=`echo $x | cut -d' ' -f2`
echo $p3

instance_slam &
x=`jobs -l | tail -1`
p4=`echo $x | cut -d' ' -f2`
echo $p4

instance_slam &
x=`jobs -l | tail -1`
p5=`echo $x | cut -d' ' -f2`
echo $p5

for i in `seq 120`; do
	ls
	sleep 1
done
kill -9 $p1 
kill -9 $p2
kill -9 $p3
kill -9 $p4
kill -9 $p5

mkdir x y z
ls x y z
rmdir x y z
exit


--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[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