Changeset View
Changeset View
Standalone View
Standalone View
head/sys/geom/mirror/g_mirror.c
Show First 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | |||||
static void g_mirror_destroy_provider(struct g_mirror_softc *sc); | static void g_mirror_destroy_provider(struct g_mirror_softc *sc); | ||||
static int g_mirror_update_disk(struct g_mirror_disk *disk, u_int state); | static int g_mirror_update_disk(struct g_mirror_disk *disk, u_int state); | ||||
static void g_mirror_update_device(struct g_mirror_softc *sc, bool force); | static void g_mirror_update_device(struct g_mirror_softc *sc, bool force); | ||||
static void g_mirror_dumpconf(struct sbuf *sb, const char *indent, | static void g_mirror_dumpconf(struct sbuf *sb, const char *indent, | ||||
struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp); | struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp); | ||||
static void g_mirror_sync_stop(struct g_mirror_disk *disk, int type); | static void g_mirror_sync_stop(struct g_mirror_disk *disk, int type); | ||||
static void g_mirror_register_request(struct bio *bp); | static void g_mirror_register_request(struct g_mirror_softc *sc, | ||||
struct bio *bp); | |||||
static void g_mirror_sync_release(struct g_mirror_softc *sc); | static void g_mirror_sync_release(struct g_mirror_softc *sc); | ||||
static const char * | static const char * | ||||
g_mirror_disk_state2str(int state) | g_mirror_disk_state2str(int state) | ||||
{ | { | ||||
switch (state) { | switch (state) { | ||||
▲ Show 20 Lines • Show All 764 Lines • ▼ Show 20 Lines | LIST_FOREACH(disk, &sc->sc_disks, d_next) { | ||||
G_MIRROR_DEBUG(2, "Disk %s (device %s) marked as dirty.", | G_MIRROR_DEBUG(2, "Disk %s (device %s) marked as dirty.", | ||||
g_mirror_get_diskname(disk), sc->sc_name); | g_mirror_get_diskname(disk), sc->sc_name); | ||||
disk->d_flags |= G_MIRROR_DISK_FLAG_DIRTY; | disk->d_flags |= G_MIRROR_DISK_FLAG_DIRTY; | ||||
g_mirror_update_metadata(disk); | g_mirror_update_metadata(disk); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
g_mirror_flush_done(struct bio *bp) | |||||
{ | |||||
struct g_mirror_softc *sc; | |||||
struct bio *pbp; | |||||
pbp = bp->bio_parent; | |||||
sc = pbp->bio_to->private; | |||||
mtx_lock(&sc->sc_done_mtx); | |||||
if (pbp->bio_error == 0) | |||||
pbp->bio_error = bp->bio_error; | |||||
pbp->bio_completed += bp->bio_completed; | |||||
pbp->bio_inbed++; | |||||
if (pbp->bio_children == pbp->bio_inbed) { | |||||
mtx_unlock(&sc->sc_done_mtx); | |||||
g_io_deliver(pbp, pbp->bio_error); | |||||
} else | |||||
mtx_unlock(&sc->sc_done_mtx); | |||||
g_destroy_bio(bp); | |||||
} | |||||
static void | |||||
g_mirror_done(struct bio *bp) | g_mirror_done(struct bio *bp) | ||||
{ | { | ||||
struct g_mirror_softc *sc; | struct g_mirror_softc *sc; | ||||
sc = bp->bio_from->geom->softc; | sc = bp->bio_from->geom->softc; | ||||
bp->bio_cflags = G_MIRROR_BIO_FLAG_REGULAR; | bp->bio_cflags = G_MIRROR_BIO_FLAG_REGULAR; | ||||
mtx_lock(&sc->sc_queue_mtx); | mtx_lock(&sc->sc_queue_mtx); | ||||
TAILQ_INSERT_TAIL(&sc->sc_queue, bp, bio_queue); | TAILQ_INSERT_TAIL(&sc->sc_queue, bp, bio_queue); | ||||
mtx_unlock(&sc->sc_queue_mtx); | mtx_unlock(&sc->sc_queue_mtx); | ||||
wakeup(sc); | wakeup(sc); | ||||
} | } | ||||
static void | static void | ||||
g_mirror_regular_request(struct bio *bp) | g_mirror_regular_request_error(struct g_mirror_softc *sc, struct bio *bp) | ||||
{ | { | ||||
struct g_mirror_softc *sc; | |||||
struct g_mirror_disk *disk; | struct g_mirror_disk *disk; | ||||
disk = bp->bio_from->private; | |||||
if (bp->bio_cmd == BIO_FLUSH && bp->bio_error == EOPNOTSUPP) | |||||
return; | |||||
if (disk == NULL) | |||||
return; | |||||
if ((disk->d_flags & G_MIRROR_DISK_FLAG_BROKEN) == 0) { | |||||
disk->d_flags |= G_MIRROR_DISK_FLAG_BROKEN; | |||||
G_MIRROR_LOGREQ(0, bp, "Request failed (error=%d).", | |||||
bp->bio_error); | |||||
} else { | |||||
G_MIRROR_LOGREQ(1, bp, "Request failed (error=%d).", | |||||
bp->bio_error); | |||||
} | |||||
if (g_mirror_disconnect_on_failure && | |||||
g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 1) { | |||||
if (bp->bio_error == ENXIO && | |||||
bp->bio_cmd == BIO_READ) | |||||
sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; | |||||
else if (bp->bio_error == ENXIO) | |||||
sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_NOW; | |||||
else | |||||
sc->sc_bump_id |= G_MIRROR_BUMP_GENID; | |||||
g_mirror_event_send(disk, G_MIRROR_DISK_STATE_DISCONNECTED, | |||||
G_MIRROR_EVENT_DONTWAIT); | |||||
} | |||||
} | |||||
static void | |||||
g_mirror_regular_request(struct g_mirror_softc *sc, struct bio *bp) | |||||
{ | |||||
struct bio *pbp; | struct bio *pbp; | ||||
g_topology_assert_not(); | g_topology_assert_not(); | ||||
KASSERT(sc->sc_provider == bp->bio_parent->bio_to, | |||||
("regular request %p with unexpected origin", bp)); | |||||
pbp = bp->bio_parent; | pbp = bp->bio_parent; | ||||
sc = pbp->bio_to->private; | |||||
bp->bio_from->index--; | bp->bio_from->index--; | ||||
if (bp->bio_cmd == BIO_WRITE || bp->bio_cmd == BIO_DELETE) | if (bp->bio_cmd == BIO_WRITE || bp->bio_cmd == BIO_DELETE) | ||||
sc->sc_writes--; | sc->sc_writes--; | ||||
disk = bp->bio_from->private; | if (bp->bio_from->private == NULL) { | ||||
if (disk == NULL) { | |||||
g_topology_lock(); | g_topology_lock(); | ||||
g_mirror_kill_consumer(sc, bp->bio_from); | g_mirror_kill_consumer(sc, bp->bio_from); | ||||
g_topology_unlock(); | g_topology_unlock(); | ||||
} | } | ||||
if (bp->bio_cmd == BIO_READ) | switch (bp->bio_cmd) { | ||||
case BIO_READ: | |||||
KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_read, | KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_read, | ||||
bp->bio_error); | bp->bio_error); | ||||
else if (bp->bio_cmd == BIO_WRITE) | break; | ||||
case BIO_WRITE: | |||||
KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_write, | KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_write, | ||||
bp->bio_error); | bp->bio_error); | ||||
break; | |||||
case BIO_DELETE: | |||||
KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_delete, | |||||
bp->bio_error); | |||||
break; | |||||
case BIO_FLUSH: | |||||
KFAIL_POINT_ERROR(DEBUG_FP, g_mirror_regular_request_flush, | |||||
bp->bio_error); | |||||
break; | |||||
} | |||||
pbp->bio_inbed++; | pbp->bio_inbed++; | ||||
KASSERT(pbp->bio_inbed <= pbp->bio_children, | KASSERT(pbp->bio_inbed <= pbp->bio_children, | ||||
("bio_inbed (%u) is bigger than bio_children (%u).", pbp->bio_inbed, | ("bio_inbed (%u) is bigger than bio_children (%u).", pbp->bio_inbed, | ||||
pbp->bio_children)); | pbp->bio_children)); | ||||
if (bp->bio_error == 0 && pbp->bio_error == 0) { | if (bp->bio_error == 0 && pbp->bio_error == 0) { | ||||
G_MIRROR_LOGREQ(3, bp, "Request delivered."); | G_MIRROR_LOGREQ(3, bp, "Request delivered."); | ||||
g_destroy_bio(bp); | g_destroy_bio(bp); | ||||
if (pbp->bio_children == pbp->bio_inbed) { | if (pbp->bio_children == pbp->bio_inbed) { | ||||
G_MIRROR_LOGREQ(3, pbp, "Request delivered."); | G_MIRROR_LOGREQ(3, pbp, "Request delivered."); | ||||
pbp->bio_completed = pbp->bio_length; | pbp->bio_completed = pbp->bio_length; | ||||
if (pbp->bio_cmd == BIO_WRITE || | if (pbp->bio_cmd == BIO_WRITE || | ||||
pbp->bio_cmd == BIO_DELETE) { | pbp->bio_cmd == BIO_DELETE) { | ||||
TAILQ_REMOVE(&sc->sc_inflight, pbp, bio_queue); | TAILQ_REMOVE(&sc->sc_inflight, pbp, bio_queue); | ||||
/* Release delayed sync requests if possible. */ | /* Release delayed sync requests if possible. */ | ||||
g_mirror_sync_release(sc); | g_mirror_sync_release(sc); | ||||
} | } | ||||
g_io_deliver(pbp, pbp->bio_error); | g_io_deliver(pbp, pbp->bio_error); | ||||
} | } | ||||
return; | return; | ||||
} else if (bp->bio_error != 0) { | } else if (bp->bio_error != 0) { | ||||
if (pbp->bio_error == 0) | if (pbp->bio_error == 0) | ||||
pbp->bio_error = bp->bio_error; | pbp->bio_error = bp->bio_error; | ||||
if (disk != NULL) { | g_mirror_regular_request_error(sc, bp); | ||||
if ((disk->d_flags & G_MIRROR_DISK_FLAG_BROKEN) == 0) { | |||||
disk->d_flags |= G_MIRROR_DISK_FLAG_BROKEN; | |||||
G_MIRROR_LOGREQ(0, bp, | |||||
"Request failed (error=%d).", | |||||
bp->bio_error); | |||||
} else { | |||||
G_MIRROR_LOGREQ(1, bp, | |||||
"Request failed (error=%d).", | |||||
bp->bio_error); | |||||
} | |||||
if (g_mirror_disconnect_on_failure && | |||||
g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) > 1) | |||||
{ | |||||
if (bp->bio_error == ENXIO && | |||||
bp->bio_cmd == BIO_READ) | |||||
sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID; | |||||
else if (bp->bio_error == ENXIO) | |||||
sc->sc_bump_id |= G_MIRROR_BUMP_SYNCID_NOW; | |||||
else | |||||
sc->sc_bump_id |= G_MIRROR_BUMP_GENID; | |||||
g_mirror_event_send(disk, | |||||
G_MIRROR_DISK_STATE_DISCONNECTED, | |||||
G_MIRROR_EVENT_DONTWAIT); | |||||
} | |||||
} | |||||
switch (pbp->bio_cmd) { | switch (pbp->bio_cmd) { | ||||
case BIO_DELETE: | case BIO_DELETE: | ||||
case BIO_WRITE: | case BIO_WRITE: | ||||
case BIO_FLUSH: | |||||
pbp->bio_inbed--; | pbp->bio_inbed--; | ||||
pbp->bio_children--; | pbp->bio_children--; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
g_destroy_bio(bp); | g_destroy_bio(bp); | ||||
switch (pbp->bio_cmd) { | switch (pbp->bio_cmd) { | ||||
case BIO_READ: | case BIO_READ: | ||||
if (pbp->bio_inbed < pbp->bio_children) | if (pbp->bio_inbed < pbp->bio_children) | ||||
break; | break; | ||||
if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1) | if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) == 1) | ||||
g_io_deliver(pbp, pbp->bio_error); | g_io_deliver(pbp, pbp->bio_error); | ||||
else { | else { | ||||
pbp->bio_error = 0; | pbp->bio_error = 0; | ||||
mtx_lock(&sc->sc_queue_mtx); | mtx_lock(&sc->sc_queue_mtx); | ||||
TAILQ_INSERT_TAIL(&sc->sc_queue, pbp, bio_queue); | TAILQ_INSERT_TAIL(&sc->sc_queue, pbp, bio_queue); | ||||
mtx_unlock(&sc->sc_queue_mtx); | mtx_unlock(&sc->sc_queue_mtx); | ||||
G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, sc); | G_MIRROR_DEBUG(4, "%s: Waking up %p.", __func__, sc); | ||||
wakeup(sc); | wakeup(sc); | ||||
} | } | ||||
break; | break; | ||||
case BIO_DELETE: | case BIO_DELETE: | ||||
case BIO_WRITE: | case BIO_WRITE: | ||||
case BIO_FLUSH: | |||||
if (pbp->bio_children == 0) { | if (pbp->bio_children == 0) { | ||||
/* | /* | ||||
* All requests failed. | * All requests failed. | ||||
*/ | */ | ||||
} else if (pbp->bio_inbed < pbp->bio_children) { | } else if (pbp->bio_inbed < pbp->bio_children) { | ||||
/* Do nothing. */ | /* Do nothing. */ | ||||
break; | break; | ||||
} else if (pbp->bio_children == pbp->bio_inbed) { | } else if (pbp->bio_children == pbp->bio_inbed) { | ||||
/* Some requests succeeded. */ | /* Some requests succeeded. */ | ||||
pbp->bio_error = 0; | pbp->bio_error = 0; | ||||
pbp->bio_completed = pbp->bio_length; | pbp->bio_completed = pbp->bio_length; | ||||
} | } | ||||
if (pbp->bio_cmd == BIO_WRITE || pbp->bio_cmd == BIO_DELETE) { | |||||
TAILQ_REMOVE(&sc->sc_inflight, pbp, bio_queue); | TAILQ_REMOVE(&sc->sc_inflight, pbp, bio_queue); | ||||
/* Release delayed sync requests if possible. */ | /* Release delayed sync requests if possible. */ | ||||
g_mirror_sync_release(sc); | g_mirror_sync_release(sc); | ||||
} | |||||
g_io_deliver(pbp, pbp->bio_error); | g_io_deliver(pbp, pbp->bio_error); | ||||
break; | break; | ||||
default: | default: | ||||
KASSERT(1 == 0, ("Invalid request: %u.", pbp->bio_cmd)); | KASSERT(1 == 0, ("Invalid request: %u.", pbp->bio_cmd)); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | g_mirror_kernel_dump(struct bio *bp) | ||||
} | } | ||||
cbp->bio_done = g_std_done; | cbp->bio_done = g_std_done; | ||||
g_io_request(cbp, disk->d_consumer); | g_io_request(cbp, disk->d_consumer); | ||||
G_MIRROR_DEBUG(1, "Kernel dump will go to %s.", | G_MIRROR_DEBUG(1, "Kernel dump will go to %s.", | ||||
g_mirror_get_diskname(disk)); | g_mirror_get_diskname(disk)); | ||||
} | } | ||||
static void | static void | ||||
g_mirror_flush(struct g_mirror_softc *sc, struct bio *bp) | |||||
{ | |||||
struct bio_queue queue; | |||||
struct g_mirror_disk *disk; | |||||
struct g_consumer *cp; | |||||
struct bio *cbp; | |||||
TAILQ_INIT(&queue); | |||||
LIST_FOREACH(disk, &sc->sc_disks, d_next) { | |||||
if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) | |||||
continue; | |||||
cbp = g_clone_bio(bp); | |||||
if (cbp == NULL) { | |||||
while ((cbp = TAILQ_FIRST(&queue)) != NULL) { | |||||
TAILQ_REMOVE(&queue, cbp, bio_queue); | |||||
g_destroy_bio(cbp); | |||||
} | |||||
if (bp->bio_error == 0) | |||||
bp->bio_error = ENOMEM; | |||||
g_io_deliver(bp, bp->bio_error); | |||||
return; | |||||
} | |||||
TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); | |||||
cbp->bio_done = g_mirror_flush_done; | |||||
cbp->bio_caller1 = disk; | |||||
cbp->bio_to = disk->d_consumer->provider; | |||||
} | |||||
while ((cbp = TAILQ_FIRST(&queue)) != NULL) { | |||||
TAILQ_REMOVE(&queue, cbp, bio_queue); | |||||
G_MIRROR_LOGREQ(3, cbp, "Sending request."); | |||||
disk = cbp->bio_caller1; | |||||
cbp->bio_caller1 = NULL; | |||||
cp = disk->d_consumer; | |||||
KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, | |||||
("Consumer %s not opened (r%dw%de%d).", cp->provider->name, | |||||
cp->acr, cp->acw, cp->ace)); | |||||
g_io_request(cbp, disk->d_consumer); | |||||
} | |||||
} | |||||
static void | |||||
g_mirror_start(struct bio *bp) | g_mirror_start(struct bio *bp) | ||||
{ | { | ||||
struct g_mirror_softc *sc; | struct g_mirror_softc *sc; | ||||
sc = bp->bio_to->private; | sc = bp->bio_to->private; | ||||
/* | /* | ||||
* If sc == NULL or there are no valid disks, provider's error | * If sc == NULL or there are no valid disks, provider's error | ||||
* should be set and g_mirror_start() should not be called at all. | * should be set and g_mirror_start() should not be called at all. | ||||
*/ | */ | ||||
KASSERT(sc != NULL && sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, | KASSERT(sc != NULL && sc->sc_state == G_MIRROR_DEVICE_STATE_RUNNING, | ||||
("Provider's error should be set (error=%d)(mirror=%s).", | ("Provider's error should be set (error=%d)(mirror=%s).", | ||||
bp->bio_to->error, bp->bio_to->name)); | bp->bio_to->error, bp->bio_to->name)); | ||||
G_MIRROR_LOGREQ(3, bp, "Request received."); | G_MIRROR_LOGREQ(3, bp, "Request received."); | ||||
switch (bp->bio_cmd) { | switch (bp->bio_cmd) { | ||||
case BIO_READ: | case BIO_READ: | ||||
case BIO_WRITE: | case BIO_WRITE: | ||||
case BIO_DELETE: | case BIO_DELETE: | ||||
break; | |||||
case BIO_FLUSH: | case BIO_FLUSH: | ||||
g_mirror_flush(sc, bp); | break; | ||||
return; | |||||
case BIO_GETATTR: | case BIO_GETATTR: | ||||
if (!strcmp(bp->bio_attribute, "GEOM::candelete")) { | if (!strcmp(bp->bio_attribute, "GEOM::candelete")) { | ||||
g_mirror_candelete(bp); | g_mirror_candelete(bp); | ||||
return; | return; | ||||
} else if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { | } else if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { | ||||
g_mirror_kernel_dump(bp); | g_mirror_kernel_dump(bp); | ||||
return; | return; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | TAILQ_FOREACH(bp, &sc->sc_inflight, bio_queue) { | ||||
rend = bp->bio_offset + bp->bio_length; | rend = bp->bio_offset + bp->bio_length; | ||||
if (rend > sstart && rstart < send) | if (rend > sstart && rstart < send) | ||||
return (true); | return (true); | ||||
} | } | ||||
return (false); | return (false); | ||||
} | } | ||||
/* | /* | ||||
* Puts request onto delayed queue. | * Puts regular request onto delayed queue. | ||||
*/ | */ | ||||
static void | static void | ||||
g_mirror_regular_delay(struct g_mirror_softc *sc, struct bio *bp) | g_mirror_regular_delay(struct g_mirror_softc *sc, struct bio *bp) | ||||
{ | { | ||||
G_MIRROR_LOGREQ(2, bp, "Delaying request."); | G_MIRROR_LOGREQ(2, bp, "Delaying request."); | ||||
TAILQ_INSERT_HEAD(&sc->sc_regular_delayed, bp, bio_queue); | TAILQ_INSERT_TAIL(&sc->sc_regular_delayed, bp, bio_queue); | ||||
} | } | ||||
/* | /* | ||||
* Puts synchronization request onto delayed queue. | * Puts synchronization request onto delayed queue. | ||||
*/ | */ | ||||
static void | static void | ||||
g_mirror_sync_delay(struct g_mirror_softc *sc, struct bio *bp) | g_mirror_sync_delay(struct g_mirror_softc *sc, struct bio *bp) | ||||
{ | { | ||||
G_MIRROR_LOGREQ(2, bp, "Delaying synchronization request."); | G_MIRROR_LOGREQ(2, bp, "Delaying synchronization request."); | ||||
TAILQ_INSERT_TAIL(&sc->sc_sync_delayed, bp, bio_queue); | TAILQ_INSERT_TAIL(&sc->sc_sync_delayed, bp, bio_queue); | ||||
} | } | ||||
/* | /* | ||||
* Releases delayed regular requests which don't collide anymore with sync | * Requeue delayed regular requests. | ||||
* requests. | |||||
*/ | */ | ||||
static void | static void | ||||
g_mirror_regular_release(struct g_mirror_softc *sc) | g_mirror_regular_release(struct g_mirror_softc *sc) | ||||
{ | { | ||||
struct bio *bp, *bp2; | struct bio *bp; | ||||
TAILQ_FOREACH_SAFE(bp, &sc->sc_regular_delayed, bio_queue, bp2) { | if ((bp = TAILQ_FIRST(&sc->sc_regular_delayed)) == NULL) | ||||
return; | |||||
if (g_mirror_sync_collision(sc, bp)) | if (g_mirror_sync_collision(sc, bp)) | ||||
continue; | return; | ||||
TAILQ_REMOVE(&sc->sc_regular_delayed, bp, bio_queue); | |||||
G_MIRROR_LOGREQ(2, bp, "Releasing delayed request (%p).", bp); | G_MIRROR_DEBUG(2, "Requeuing regular requests after collision."); | ||||
mtx_lock(&sc->sc_queue_mtx); | mtx_lock(&sc->sc_queue_mtx); | ||||
TAILQ_INSERT_HEAD(&sc->sc_queue, bp, bio_queue); | TAILQ_CONCAT(&sc->sc_regular_delayed, &sc->sc_queue, bio_queue); | ||||
TAILQ_SWAP(&sc->sc_regular_delayed, &sc->sc_queue, bio, bio_queue); | |||||
mtx_unlock(&sc->sc_queue_mtx); | mtx_unlock(&sc->sc_queue_mtx); | ||||
} | } | ||||
} | |||||
/* | /* | ||||
* Releases delayed sync requests which don't collide anymore with regular | * Releases delayed sync requests which don't collide anymore with regular | ||||
* requests. | * requests. | ||||
*/ | */ | ||||
static void | static void | ||||
g_mirror_sync_release(struct g_mirror_softc *sc) | g_mirror_sync_release(struct g_mirror_softc *sc) | ||||
{ | { | ||||
Show All 30 Lines | |||||
/* | /* | ||||
* Handle synchronization requests. | * Handle synchronization requests. | ||||
* Every synchronization request is two-steps process: first, READ request is | * Every synchronization request is two-steps process: first, READ request is | ||||
* send to active provider and then WRITE request (with read data) to the provider | * send to active provider and then WRITE request (with read data) to the provider | ||||
* being synchronized. When WRITE is finished, new synchronization request is | * being synchronized. When WRITE is finished, new synchronization request is | ||||
* send. | * send. | ||||
*/ | */ | ||||
static void | static void | ||||
g_mirror_sync_request(struct bio *bp) | g_mirror_sync_request(struct g_mirror_softc *sc, struct bio *bp) | ||||
{ | { | ||||
struct g_mirror_softc *sc; | |||||
struct g_mirror_disk *disk; | struct g_mirror_disk *disk; | ||||
struct g_mirror_disk_sync *sync; | struct g_mirror_disk_sync *sync; | ||||
KASSERT((bp->bio_cmd == BIO_READ && | |||||
bp->bio_from->geom == sc->sc_sync.ds_geom) || | |||||
(bp->bio_cmd == BIO_WRITE && bp->bio_from->geom == sc->sc_geom), | |||||
("Sync BIO %p with unexpected origin", bp)); | |||||
bp->bio_from->index--; | bp->bio_from->index--; | ||||
sc = bp->bio_from->geom->softc; | |||||
disk = bp->bio_from->private; | disk = bp->bio_from->private; | ||||
if (disk == NULL) { | if (disk == NULL) { | ||||
sx_xunlock(&sc->sc_lock); /* Avoid recursion on sc_lock. */ | sx_xunlock(&sc->sc_lock); /* Avoid recursion on sc_lock. */ | ||||
g_topology_lock(); | g_topology_lock(); | ||||
g_mirror_kill_consumer(sc, bp->bio_from); | g_mirror_kill_consumer(sc, bp->bio_from); | ||||
g_topology_unlock(); | g_topology_unlock(); | ||||
g_mirror_sync_request_free(NULL, bp); | g_mirror_sync_request_free(NULL, bp); | ||||
sx_xlock(&sc->sc_lock); | sx_xlock(&sc->sc_lock); | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | case BIO_WRITE: | ||||
/* | /* | ||||
* Delay the request if it is colliding with a regular request. | * Delay the request if it is colliding with a regular request. | ||||
*/ | */ | ||||
if (g_mirror_regular_collision(sc, bp)) | if (g_mirror_regular_collision(sc, bp)) | ||||
g_mirror_sync_delay(sc, bp); | g_mirror_sync_delay(sc, bp); | ||||
else | else | ||||
g_io_request(bp, sync->ds_consumer); | g_io_request(bp, sync->ds_consumer); | ||||
/* Release delayed requests if possible. */ | /* Requeue delayed requests if possible. */ | ||||
g_mirror_regular_release(sc); | g_mirror_regular_release(sc); | ||||
/* Find the smallest offset */ | /* Find the smallest offset */ | ||||
offset = sc->sc_mediasize; | offset = sc->sc_mediasize; | ||||
for (i = 0; i < g_mirror_syncreqs; i++) { | for (i = 0; i < g_mirror_syncreqs; i++) { | ||||
bp = sync->ds_bios[i]; | bp = sync->ds_bios[i]; | ||||
if (bp != NULL && bp->bio_offset < offset) | if (bp != NULL && bp->bio_offset < offset) | ||||
offset = bp->bio_offset; | offset = bp->bio_offset; | ||||
▲ Show 20 Lines • Show All 211 Lines • ▼ Show 20 Lines | KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, | ||||
("Consumer %s not opened (r%dw%de%d).", cp->provider->name, | ("Consumer %s not opened (r%dw%de%d).", cp->provider->name, | ||||
cp->acr, cp->acw, cp->ace)); | cp->acr, cp->acw, cp->ace)); | ||||
disk->d_consumer->index++; | disk->d_consumer->index++; | ||||
g_io_request(cbp, disk->d_consumer); | g_io_request(cbp, disk->d_consumer); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
g_mirror_register_request(struct bio *bp) | g_mirror_register_request(struct g_mirror_softc *sc, struct bio *bp) | ||||
{ | { | ||||
struct g_mirror_softc *sc; | struct bio_queue queue; | ||||
struct bio *cbp; | |||||
struct g_consumer *cp; | |||||
struct g_mirror_disk *disk; | |||||
sc = bp->bio_to->private; | sx_assert(&sc->sc_lock, SA_XLOCKED); | ||||
/* | |||||
* To avoid ordering issues, if a write is deferred because of a | |||||
* collision with a sync request, all I/O is deferred until that | |||||
* write is initiated. | |||||
*/ | |||||
if (bp->bio_from->geom != sc->sc_sync.ds_geom && | |||||
!TAILQ_EMPTY(&sc->sc_regular_delayed)) { | |||||
g_mirror_regular_delay(sc, bp); | |||||
return; | |||||
} | |||||
switch (bp->bio_cmd) { | switch (bp->bio_cmd) { | ||||
case BIO_READ: | case BIO_READ: | ||||
switch (sc->sc_balance) { | switch (sc->sc_balance) { | ||||
case G_MIRROR_BALANCE_LOAD: | case G_MIRROR_BALANCE_LOAD: | ||||
g_mirror_request_load(sc, bp); | g_mirror_request_load(sc, bp); | ||||
break; | break; | ||||
case G_MIRROR_BALANCE_PREFER: | case G_MIRROR_BALANCE_PREFER: | ||||
g_mirror_request_prefer(sc, bp); | g_mirror_request_prefer(sc, bp); | ||||
break; | break; | ||||
case G_MIRROR_BALANCE_ROUND_ROBIN: | case G_MIRROR_BALANCE_ROUND_ROBIN: | ||||
g_mirror_request_round_robin(sc, bp); | g_mirror_request_round_robin(sc, bp); | ||||
break; | break; | ||||
case G_MIRROR_BALANCE_SPLIT: | case G_MIRROR_BALANCE_SPLIT: | ||||
g_mirror_request_split(sc, bp); | g_mirror_request_split(sc, bp); | ||||
break; | break; | ||||
} | } | ||||
return; | return; | ||||
case BIO_WRITE: | case BIO_WRITE: | ||||
case BIO_DELETE: | case BIO_DELETE: | ||||
{ | |||||
struct bio_queue queue; | |||||
struct g_mirror_disk *disk; | |||||
struct g_mirror_disk_sync *sync; | |||||
struct g_consumer *cp; | |||||
struct bio *cbp; | |||||
/* | /* | ||||
* Delay the request if it is colliding with a synchronization | * Delay the request if it is colliding with a synchronization | ||||
* request. | * request. | ||||
*/ | */ | ||||
if (g_mirror_sync_collision(sc, bp)) { | if (g_mirror_sync_collision(sc, bp)) { | ||||
g_mirror_regular_delay(sc, bp); | g_mirror_regular_delay(sc, bp); | ||||
return; | return; | ||||
} | } | ||||
Show All 12 Lines | case BIO_DELETE: | ||||
} | } | ||||
/* | /* | ||||
* Allocate all bios before sending any request, so we can | * Allocate all bios before sending any request, so we can | ||||
* return ENOMEM in nice and clean way. | * return ENOMEM in nice and clean way. | ||||
*/ | */ | ||||
TAILQ_INIT(&queue); | TAILQ_INIT(&queue); | ||||
LIST_FOREACH(disk, &sc->sc_disks, d_next) { | LIST_FOREACH(disk, &sc->sc_disks, d_next) { | ||||
sync = &disk->d_sync; | |||||
switch (disk->d_state) { | switch (disk->d_state) { | ||||
case G_MIRROR_DISK_STATE_ACTIVE: | case G_MIRROR_DISK_STATE_ACTIVE: | ||||
break; | break; | ||||
case G_MIRROR_DISK_STATE_SYNCHRONIZING: | case G_MIRROR_DISK_STATE_SYNCHRONIZING: | ||||
if (bp->bio_offset >= sync->ds_offset) | if (bp->bio_offset >= disk->d_sync.ds_offset) | ||||
continue; | continue; | ||||
break; | break; | ||||
default: | default: | ||||
continue; | continue; | ||||
} | } | ||||
if (bp->bio_cmd == BIO_DELETE && | if (bp->bio_cmd == BIO_DELETE && | ||||
(disk->d_flags & G_MIRROR_DISK_FLAG_CANDELETE) == 0) | (disk->d_flags & G_MIRROR_DISK_FLAG_CANDELETE) == 0) | ||||
continue; | continue; | ||||
Show All 13 Lines | LIST_FOREACH(disk, &sc->sc_disks, d_next) { | ||||
cp = disk->d_consumer; | cp = disk->d_consumer; | ||||
cbp->bio_caller1 = cp; | cbp->bio_caller1 = cp; | ||||
cbp->bio_to = cp->provider; | cbp->bio_to = cp->provider; | ||||
KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, | KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, | ||||
("Consumer %s not opened (r%dw%de%d).", | ("Consumer %s not opened (r%dw%de%d).", | ||||
cp->provider->name, cp->acr, cp->acw, cp->ace)); | cp->provider->name, cp->acr, cp->acw, cp->ace)); | ||||
} | } | ||||
if (TAILQ_EMPTY(&queue)) { | if (TAILQ_EMPTY(&queue)) { | ||||
KASSERT(bp->bio_cmd == BIO_DELETE, | |||||
("No consumers for regular request %p", bp)); | |||||
g_io_deliver(bp, EOPNOTSUPP); | g_io_deliver(bp, EOPNOTSUPP); | ||||
return; | return; | ||||
} | } | ||||
while ((cbp = TAILQ_FIRST(&queue)) != NULL) { | while ((cbp = TAILQ_FIRST(&queue)) != NULL) { | ||||
G_MIRROR_LOGREQ(3, cbp, "Sending request."); | G_MIRROR_LOGREQ(3, cbp, "Sending request."); | ||||
TAILQ_REMOVE(&queue, cbp, bio_queue); | TAILQ_REMOVE(&queue, cbp, bio_queue); | ||||
cp = cbp->bio_caller1; | cp = cbp->bio_caller1; | ||||
cbp->bio_caller1 = NULL; | cbp->bio_caller1 = NULL; | ||||
cp->index++; | cp->index++; | ||||
sc->sc_writes++; | sc->sc_writes++; | ||||
g_io_request(cbp, cp); | g_io_request(cbp, cp); | ||||
} | } | ||||
/* | /* | ||||
* Put request onto inflight queue, so we can check if new | * Put request onto inflight queue, so we can check if new | ||||
* synchronization requests don't collide with it. | * synchronization requests don't collide with it. | ||||
*/ | */ | ||||
TAILQ_INSERT_TAIL(&sc->sc_inflight, bp, bio_queue); | TAILQ_INSERT_TAIL(&sc->sc_inflight, bp, bio_queue); | ||||
return; | return; | ||||
case BIO_FLUSH: | |||||
TAILQ_INIT(&queue); | |||||
LIST_FOREACH(disk, &sc->sc_disks, d_next) { | |||||
if (disk->d_state != G_MIRROR_DISK_STATE_ACTIVE) | |||||
continue; | |||||
cbp = g_clone_bio(bp); | |||||
if (cbp == NULL) { | |||||
while ((cbp = TAILQ_FIRST(&queue)) != NULL) { | |||||
TAILQ_REMOVE(&queue, cbp, bio_queue); | |||||
g_destroy_bio(cbp); | |||||
} | } | ||||
if (bp->bio_error == 0) | |||||
bp->bio_error = ENOMEM; | |||||
g_io_deliver(bp, bp->bio_error); | |||||
return; | |||||
} | |||||
TAILQ_INSERT_TAIL(&queue, cbp, bio_queue); | |||||
cbp->bio_done = g_mirror_done; | |||||
cbp->bio_caller1 = disk; | |||||
cbp->bio_to = disk->d_consumer->provider; | |||||
} | |||||
KASSERT(!TAILQ_EMPTY(&queue), | |||||
("No consumers for regular request %p", bp)); | |||||
while ((cbp = TAILQ_FIRST(&queue)) != NULL) { | |||||
G_MIRROR_LOGREQ(3, cbp, "Sending request."); | |||||
TAILQ_REMOVE(&queue, cbp, bio_queue); | |||||
disk = cbp->bio_caller1; | |||||
cbp->bio_caller1 = NULL; | |||||
cp = disk->d_consumer; | |||||
KASSERT(cp->acr >= 1 && cp->acw >= 1 && cp->ace >= 1, | |||||
("Consumer %s not opened (r%dw%de%d).", cp->provider->name, | |||||
cp->acr, cp->acw, cp->ace)); | |||||
cp->index++; | |||||
g_io_request(cbp, cp); | |||||
} | |||||
break; | |||||
default: | default: | ||||
KASSERT(1 == 0, ("Invalid command here: %u (device=%s)", | KASSERT(1 == 0, ("Invalid command here: %u (device=%s)", | ||||
bp->bio_cmd, sc->sc_name)); | bp->bio_cmd, sc->sc_name)); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | if (ep != NULL) { | ||||
curthread->td_pflags &= ~TDP_GEOM; | curthread->td_pflags &= ~TDP_GEOM; | ||||
G_MIRROR_DEBUG(1, "Thread exiting."); | G_MIRROR_DEBUG(1, "Thread exiting."); | ||||
kproc_exit(0); | kproc_exit(0); | ||||
} | } | ||||
} | } | ||||
G_MIRROR_DEBUG(5, "%s: I'm here 1.", __func__); | G_MIRROR_DEBUG(5, "%s: I'm here 1.", __func__); | ||||
continue; | continue; | ||||
} | } | ||||
/* | /* | ||||
* Check if we can mark array as CLEAN and if we can't take | * Check if we can mark array as CLEAN and if we can't take | ||||
* how much seconds should we wait. | * how much seconds should we wait. | ||||
*/ | */ | ||||
timeout = g_mirror_idle(sc, -1); | timeout = g_mirror_idle(sc, -1); | ||||
/* | /* | ||||
* Now I/O requests. | * Handle I/O requests. | ||||
*/ | */ | ||||
/* Get first request from the queue. */ | |||||
mtx_lock(&sc->sc_queue_mtx); | mtx_lock(&sc->sc_queue_mtx); | ||||
bp = TAILQ_FIRST(&sc->sc_queue); | bp = TAILQ_FIRST(&sc->sc_queue); | ||||
if (bp != NULL) | if (bp != NULL) | ||||
TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue); | TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue); | ||||
else { | else { | ||||
if ((sc->sc_flags & | if ((sc->sc_flags & | ||||
G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { | G_MIRROR_DEVICE_FLAG_DESTROY) != 0) { | ||||
mtx_unlock(&sc->sc_queue_mtx); | mtx_unlock(&sc->sc_queue_mtx); | ||||
Show All 16 Lines | else { | ||||
sx_xlock(&sc->sc_lock); | sx_xlock(&sc->sc_lock); | ||||
G_MIRROR_DEBUG(5, "%s: I'm here 4.", __func__); | G_MIRROR_DEBUG(5, "%s: I'm here 4.", __func__); | ||||
continue; | continue; | ||||
} | } | ||||
mtx_unlock(&sc->sc_queue_mtx); | mtx_unlock(&sc->sc_queue_mtx); | ||||
if (bp->bio_from->geom == sc->sc_sync.ds_geom && | if (bp->bio_from->geom == sc->sc_sync.ds_geom && | ||||
(bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) { | (bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) { | ||||
g_mirror_sync_request(bp); /* READ */ | /* | ||||
* Handle completion of the first half (the read) of a | |||||
* block synchronization operation. | |||||
*/ | |||||
g_mirror_sync_request(sc, bp); | |||||
} else if (bp->bio_to != sc->sc_provider) { | } else if (bp->bio_to != sc->sc_provider) { | ||||
if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_REGULAR) != 0) | if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_REGULAR) != 0) | ||||
g_mirror_regular_request(bp); | /* | ||||
* Handle completion of a regular I/O request. | |||||
*/ | |||||
g_mirror_regular_request(sc, bp); | |||||
else if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) | else if ((bp->bio_cflags & G_MIRROR_BIO_FLAG_SYNC) != 0) | ||||
g_mirror_sync_request(bp); /* WRITE */ | /* | ||||
* Handle completion of the second half (the | |||||
* write) of a block synchronization operation. | |||||
*/ | |||||
g_mirror_sync_request(sc, bp); | |||||
else { | else { | ||||
KASSERT(0, | KASSERT(0, | ||||
("Invalid request cflags=0x%hx to=%s.", | ("Invalid request cflags=0x%hx to=%s.", | ||||
bp->bio_cflags, bp->bio_to->name)); | bp->bio_cflags, bp->bio_to->name)); | ||||
} | } | ||||
} else { | } else { | ||||
g_mirror_register_request(bp); | /* | ||||
* Initiate an I/O request. | |||||
*/ | |||||
g_mirror_register_request(sc, bp); | |||||
} | } | ||||
G_MIRROR_DEBUG(5, "%s: I'm here 9.", __func__); | G_MIRROR_DEBUG(5, "%s: I'm here 9.", __func__); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
g_mirror_update_idle(struct g_mirror_softc *sc, struct g_mirror_disk *disk) | g_mirror_update_idle(struct g_mirror_softc *sc, struct g_mirror_disk *disk) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 1,458 Lines • Show Last 20 Lines |