Make g_wither_washer() to not loop by itself, but only when there was some

more topology change done that may require its attention.  Add few missing
g_do_wither() calls in respective places to signal it.

This fixes potential infinite loop here when some provider is withered, but
still opened or connected for some reason and so can not be destroyed.  For
example, see r227009 and r227510.
This commit is contained in:
Alexander Motin 2013-03-24 03:15:20 +00:00
parent 317e774722
commit 50199fa0d0
3 changed files with 13 additions and 29 deletions

View File

@ -273,21 +273,16 @@ one_event(void)
void
g_run_events()
{
int i;
for (;;) {
g_topology_lock();
while (one_event())
;
mtx_assert(&g_eventlock, MA_OWNED);
i = g_wither_work;
if (i) {
if (g_wither_work) {
g_wither_work = 0;
mtx_unlock(&g_eventlock);
while (i) {
i = g_wither_washer();
g_wither_work = i & 1;
i &= 2;
}
g_wither_washer();
g_topology_unlock();
} else {
g_topology_unlock();

View File

@ -65,7 +65,7 @@ void g_do_wither(void);
/* geom_subr.c */
extern struct class_list_head g_classes;
extern char *g_wait_event, *g_wait_sim, *g_wait_up, *g_wait_down;
int g_wither_washer(void);
void g_wither_washer(void);
/* geom_io.c */
void g_io_init(void);

View File

@ -437,20 +437,16 @@ g_wither_geom_close(struct g_geom *gp, int error)
/*
* This function is called (repeatedly) until we cant wash away more
* withered bits at present. Return value contains two bits. Bit 0
* set means "withering stuff we can't wash now", bit 1 means "call
* me again, there may be stuff I didn't get the first time around.
* withered bits at present.
*/
int
void
g_wither_washer()
{
struct g_class *mp;
struct g_geom *gp, *gp2;
struct g_provider *pp, *pp2;
struct g_consumer *cp, *cp2;
int result;
result = 0;
g_topology_assert();
LIST_FOREACH(mp, &g_classes, class) {
LIST_FOREACH_SAFE(gp, &mp->geom, geom, gp2) {
@ -459,35 +455,25 @@ g_wither_washer()
continue;
if (LIST_EMPTY(&pp->consumers))
g_destroy_provider(pp);
else
result |= 1;
}
if (!(gp->flags & G_GEOM_WITHER))
continue;
LIST_FOREACH_SAFE(pp, &gp->provider, provider, pp2) {
if (LIST_EMPTY(&pp->consumers))
g_destroy_provider(pp);
else
result |= 1;
}
LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp2) {
if (cp->acr || cp->acw || cp->ace) {
result |= 1;
if (cp->acr || cp->acw || cp->ace)
continue;
}
if (cp->provider != NULL)
g_detach(cp);
g_destroy_consumer(cp);
result |= 2;
}
if (LIST_EMPTY(&gp->provider) &&
LIST_EMPTY(&gp->consumer))
g_destroy_geom(gp);
else
result |= 1;
}
}
return (result);
}
struct g_consumer *
@ -847,9 +833,9 @@ g_detach(struct g_consumer *cp)
pp = cp->provider;
LIST_REMOVE(cp, consumers);
cp->provider = NULL;
if (pp->geom->flags & G_GEOM_WITHER)
g_do_wither();
else if (pp->flags & G_PF_WITHER)
if ((cp->geom->flags & G_GEOM_WITHER) ||
(pp->geom->flags & G_GEOM_WITHER) ||
(pp->flags & G_PF_WITHER))
g_do_wither();
redo_rank(cp->geom);
}
@ -948,6 +934,9 @@ g_access(struct g_consumer *cp, int dcr, int dcw, int dce)
if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)
KASSERT(pp->sectorsize > 0,
("Provider %s lacks sectorsize", pp->name));
if ((cp->geom->flags & G_GEOM_WITHER) &&
cp->acr == 0 && cp->acw == 0 && cp->ace == 0)
g_do_wither();
}
return (error);
}