Skip to content
Snippets Groups Projects
Commit fb91b7ac authored by Alessandro Rubini's avatar Alessandro Rubini
Browse files

bugfix for buffer change (bug exposed by this branch)


I'm unable to backport this commit to the proper place (too many
conflicts). Anyways, this fixes an issue related to changing the
buffer type (nothing bad happens if you don't change buffer type).

The bug is exposed by the self-timed csets: when a buffer type is
being changed, the trigger may be armed. This means that a block is
being used by the device, but the hosting buffer instance is being
destroyed.

This commit aborts a trigger and keeps it disabled while the buffer
is being changed. Re-enabling later, unless it was already disabled.

Even without self-timed support there was a tiny window for the race
to appear. Now it's fixed forever.

Signed-off-by: default avatarAlessandro Rubini <rubini@gnudd.com>
Acked-by: default avatarFederico Vaga <federico.vaga@gmail.com>
parent 740978cf
Branches
Tags
No related merge requests found
......@@ -342,15 +342,28 @@ out_put:
return err;
}
/*
* This is only called in process context (through a sysfs operation)
*
* The code is very similar to the change of trigger above, and it must
* temporary disable the trigger. It will remember whether it was disabled
* when entering this thing, but later we'll have a "-" to keep it disabled.
*/
int zio_change_current_buffer(struct zio_cset *cset, char *name)
{
struct zio_buffer_type *zbuf, *zbuf_old = cset->zbuf;
struct zio_ti *ti = cset->ti;
struct zio_bi **bi_vector;
unsigned long flags, tflags;
int i, j, err;
pr_debug("%s\n", __func__);
/* FIXME: parse a leading "-" to mean we want it disabled */
if (unlikely(strcmp(name, cset->zbuf->head.name) == 0))
return 0; /* it is the current buffer */
zbuf = zio_buffer_get(cset, name);
if (IS_ERR(zbuf))
return PTR_ERR(zbuf);
......@@ -359,8 +372,9 @@ int zio_change_current_buffer(struct zio_cset *cset, char *name)
GFP_KERNEL);
if (!bi_vector) {
err = -ENOMEM;
goto out;
goto out_put;
}
/* Create a new buffer instance for each channel of the cset */
for (i = 0; i < cset->n_chan; ++i) {
bi_vector[i] = __bi_create_and_init(zbuf, &cset->chan[i]);
......@@ -377,6 +391,7 @@ int zio_change_current_buffer(struct zio_cset *cset, char *name)
goto out_create;
}
}
tflags = zio_trigger_abort_disable(cset, 1);
for (i = 0; i < cset->n_chan; ++i) {
/* Delete old buffer instance */
......@@ -390,11 +405,19 @@ int zio_change_current_buffer(struct zio_cset *cset, char *name)
WARN(1, "%s: cannot rename buffer folder for"
" cset%d:chan%d\n", __func__, cset->index, i);
}
kfree(bi_vector);
cset->zbuf = zbuf;
kfree(bi_vector);
zio_buffer_put(zbuf_old, cset->zdev->owner);
/* exit the disabled region: keep it disabled if needed */
spin_lock_irqsave(&cset->lock, flags);
ti->flags = (ti->flags & ~ZIO_DISABLED) | tflags;
spin_unlock_irqrestore(&cset->lock, flags);
/* Finally, arm the trigger if so needed */
if (zio_cset_is_self_timed(cset))
zio_arm_trigger(ti);
return 0;
out_create:
......@@ -403,7 +426,7 @@ out_create:
__bi_destroy(zbuf, bi_vector[j]);
}
kfree(bi_vector);
out:
out_put:
zio_buffer_put(zbuf, cset->zdev->owner);
return err;
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment