Changeset View
Standalone View
sys/geom/eli/g_eli_integrity.c
Show All 32 Lines | |||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/linker.h> | #include <sys/linker.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/bio.h> | #include <sys/bio.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/malloc.h> | |||||
#include <sys/kthread.h> | #include <sys/kthread.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/sched.h> | #include <sys/sched.h> | ||||
#include <sys/smp.h> | #include <sys/smp.h> | ||||
#include <sys/vnode.h> | #include <sys/vnode.h> | ||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Code paths: | * Code paths: | ||||
* BIO_READ: | * BIO_READ: | ||||
* g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> g_eli_auth_run -> g_eli_auth_read_done -> g_io_deliver | * g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> g_eli_auth_run -> g_eli_auth_read_done -> g_io_deliver | ||||
* BIO_WRITE: | * BIO_WRITE: | ||||
* g_eli_start -> g_eli_auth_run -> g_eli_auth_write_done -> g_io_request -> g_eli_write_done -> g_io_deliver | * g_eli_start -> g_eli_auth_run -> g_eli_auth_write_done -> g_io_request -> g_eli_write_done -> g_io_deliver | ||||
*/ | */ | ||||
MALLOC_DECLARE(M_ELI); | |||||
/* | /* | ||||
* Here we generate key for HMAC. Every sector has its own HMAC key, so it is | * Here we generate key for HMAC. Every sector has its own HMAC key, so it is | ||||
* not possible to copy sectors. | * not possible to copy sectors. | ||||
* We cannot depend on fact, that every sector has its own IV, because different | * We cannot depend on fact, that every sector has its own IV, because different | ||||
* IV doesn't change HMAC, when we use encrypt-then-authenticate method. | * IV doesn't change HMAC, when we use encrypt-then-authenticate method. | ||||
*/ | */ | ||||
static void | static void | ||||
g_eli_auth_keygen(struct g_eli_softc *sc, off_t offset, u_char *key) | g_eli_auth_keygen(struct g_eli_softc *sc, off_t offset, u_char *key) | ||||
▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | if (bp->bio_error == 0) { | ||||
} | } | ||||
/* Report previous corruption if there was one. */ | /* Report previous corruption if there was one. */ | ||||
if (coroff != -1) { | if (coroff != -1) { | ||||
G_ELI_DEBUG(0, "%s: Failed to authenticate %jd " | G_ELI_DEBUG(0, "%s: Failed to authenticate %jd " | ||||
"bytes of data at offset %jd.", | "bytes of data at offset %jd.", | ||||
sc->sc_name, (intmax_t)corsize, (intmax_t)coroff); | sc->sc_name, (intmax_t)corsize, (intmax_t)coroff); | ||||
} | } | ||||
} | } | ||||
free(bp->bio_driver2, M_ELI); | g_eli_free_data(bp); | ||||
bp->bio_driver2 = NULL; | |||||
if (bp->bio_error != 0) { | if (bp->bio_error != 0) { | ||||
if (bp->bio_error != EINTEGRITY) { | if (bp->bio_error != EINTEGRITY) { | ||||
G_ELI_LOGREQ(0, bp, | G_ELI_LOGREQ(0, bp, | ||||
"Crypto READ request failed (error=%d).", | "Crypto READ request failed (error=%d).", | ||||
bp->bio_error); | bp->bio_error); | ||||
} | } | ||||
bp->bio_completed = 0; | bp->bio_completed = 0; | ||||
} | } | ||||
Show All 40 Lines | g_eli_auth_write_done(struct cryptop *crp) | ||||
/* | /* | ||||
* All sectors are already encrypted? | * All sectors are already encrypted? | ||||
*/ | */ | ||||
if (bp->bio_inbed < bp->bio_children) | if (bp->bio_inbed < bp->bio_children) | ||||
return (0); | return (0); | ||||
if (bp->bio_error != 0) { | if (bp->bio_error != 0) { | ||||
G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", | G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", | ||||
bp->bio_error); | bp->bio_error); | ||||
free(bp->bio_driver2, M_ELI); | g_eli_free_data(bp); | ||||
bp->bio_driver2 = NULL; | |||||
cbp = bp->bio_driver1; | cbp = bp->bio_driver1; | ||||
bp->bio_driver1 = NULL; | bp->bio_driver1 = NULL; | ||||
g_destroy_bio(cbp); | g_destroy_bio(cbp); | ||||
g_io_deliver(bp, bp->bio_error); | g_io_deliver(bp, bp->bio_error); | ||||
atomic_subtract_int(&sc->sc_inflight, 1); | atomic_subtract_int(&sc->sc_inflight, 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
cp = LIST_FIRST(&sc->sc_geom->consumer); | cp = LIST_FIRST(&sc->sc_geom->consumer); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
void | void | ||||
g_eli_auth_read(struct g_eli_softc *sc, struct bio *bp) | g_eli_auth_read(struct g_eli_softc *sc, struct bio *bp) | ||||
{ | { | ||||
struct g_consumer *cp; | struct g_consumer *cp; | ||||
struct bio *cbp, *cbp2; | struct bio *cbp, *cbp2; | ||||
size_t size; | size_t size; | ||||
off_t nsec; | off_t nsec; | ||||
bp->bio_pflags = 0; | G_ELI_SETWORKER(bp->bio_pflags, 0); | ||||
cp = LIST_FIRST(&sc->sc_geom->consumer); | cp = LIST_FIRST(&sc->sc_geom->consumer); | ||||
cbp = bp->bio_driver1; | cbp = bp->bio_driver1; | ||||
bp->bio_driver1 = NULL; | bp->bio_driver1 = NULL; | ||||
cbp->bio_to = cp->provider; | cbp->bio_to = cp->provider; | ||||
cbp->bio_done = g_eli_read_done; | cbp->bio_done = g_eli_read_done; | ||||
/* Number of sectors from decrypted provider, eg. 1. */ | /* Number of sectors from decrypted provider, eg. 1. */ | ||||
nsec = bp->bio_length / bp->bio_to->sectorsize; | nsec = bp->bio_length / bp->bio_to->sectorsize; | ||||
/* Number of sectors from encrypted provider, eg. 9. */ | /* Number of sectors from encrypted provider, eg. 9. */ | ||||
nsec = (nsec * sc->sc_bytes_per_sector) / cp->provider->sectorsize; | nsec = (nsec * sc->sc_bytes_per_sector) / cp->provider->sectorsize; | ||||
cbp->bio_length = cp->provider->sectorsize * nsec; | cbp->bio_length = cp->provider->sectorsize * nsec; | ||||
size = cbp->bio_length; | size = cbp->bio_length; | ||||
size += sizeof(int) * nsec; | size += sizeof(int) * nsec; | ||||
size += G_ELI_AUTH_SECKEYLEN * nsec; | size += G_ELI_AUTH_SECKEYLEN * nsec; | ||||
cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; | cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; | ||||
bp->bio_driver2 = malloc(size, M_ELI, M_WAITOK); | if (!g_eli_alloc_data(bp, size)) { | ||||
G_ELI_LOGREQ(0, bp, "Crypto auth read request failed (ENOMEM)"); | |||||
jhb: I really worry that while this may be ok for swap to get `ENOMEM` errors, it will not be ok for… | |||||
Done Inline ActionsOK. If I change the default for kern.geom.eli.blocking_malloc to true, that will maintain the current (blocking) behavior for everything except swap. I really feel strongly that swap should not block. I don't feel strongly about anything else, so it will be easy to convince me to leave the current behavior alone. jtl: OK. If I change the default for kern.geom.eli.blocking_malloc to true, that will maintain the… | |||||
Not Done Inline ActionsENOMEM is fine. geom will retry it. UFS won't see that error, so it won't trigger chs' latest work. imp: ENOMEM is fine. geom will retry it. UFS won't see that error, so it won't trigger chs' latest… | |||||
Not Done Inline ActionsThough I agree we should make sure that my bold assertions work in practice :) imp: Though I agree we should make sure that my bold assertions work in practice :) | |||||
Not Done Inline ActionsENOMEM processing is very special in the geom. It returns error to the upper layer but also set the pace variable which stops processing of the new bios for some time. I remember this because I was not able to make ENOMEM error work as I need for situations where transient map was full for unmapped bios. I ended up with some other error which avoided this stall mechanics and allowed the transient map to free some space. kib: ENOMEM processing is very special in the geom. It returns error to the upper layer but also… | |||||
g_destroy_bio(cbp); | |||||
bp->bio_error = ENOMEM; | |||||
g_io_deliver(bp, bp->bio_error); | |||||
atomic_subtract_int(&sc->sc_inflight, 1); | |||||
return; | |||||
} | |||||
cbp->bio_data = bp->bio_driver2; | cbp->bio_data = bp->bio_driver2; | ||||
/* Clear the error array. */ | /* Clear the error array. */ | ||||
memset((char *)bp->bio_driver2 + cbp->bio_length, 0, | memset((char *)bp->bio_driver2 + cbp->bio_length, 0, | ||||
sizeof(int) * nsec); | sizeof(int) * nsec); | ||||
/* | /* | ||||
* We read more than what is requested, so we have to be ready to read | * We read more than what is requested, so we have to be ready to read | ||||
Show All 38 Lines | g_eli_auth_run(struct g_eli_worker *wr, struct bio *bp) | ||||
u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize; | u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize; | ||||
off_t dstoff; | off_t dstoff; | ||||
u_char *p, *data, *authkey, *plaindata; | u_char *p, *data, *authkey, *plaindata; | ||||
int error; | int error; | ||||
bool batch; | bool batch; | ||||
G_ELI_LOGREQ(3, bp, "%s", __func__); | G_ELI_LOGREQ(3, bp, "%s", __func__); | ||||
bp->bio_pflags = wr->w_number; | G_ELI_SETWORKER(bp->bio_pflags, wr->w_number); | ||||
sc = wr->w_softc; | sc = wr->w_softc; | ||||
/* Sectorsize of decrypted provider eg. 4096. */ | /* Sectorsize of decrypted provider eg. 4096. */ | ||||
decr_secsize = bp->bio_to->sectorsize; | decr_secsize = bp->bio_to->sectorsize; | ||||
/* The real sectorsize of encrypted provider, eg. 512. */ | /* The real sectorsize of encrypted provider, eg. 512. */ | ||||
encr_secsize = LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize; | encr_secsize = LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize; | ||||
/* Number of data bytes in one encrypted sector, eg. 480. */ | /* Number of data bytes in one encrypted sector, eg. 480. */ | ||||
data_secsize = sc->sc_data_per_sector; | data_secsize = sc->sc_data_per_sector; | ||||
/* Number of sectors from decrypted provider, eg. 2. */ | /* Number of sectors from decrypted provider, eg. 2. */ | ||||
Show All 11 Lines | if (bp->bio_cmd == BIO_READ) { | ||||
p = data + encr_secsize * nsec; | p = data + encr_secsize * nsec; | ||||
p += sizeof(int) * nsec; | p += sizeof(int) * nsec; | ||||
} else { | } else { | ||||
size_t size; | size_t size; | ||||
size = encr_secsize * nsec; | size = encr_secsize * nsec; | ||||
size += G_ELI_AUTH_SECKEYLEN * nsec; | size += G_ELI_AUTH_SECKEYLEN * nsec; | ||||
size += sizeof(uintptr_t); /* Space for alignment. */ | size += sizeof(uintptr_t); /* Space for alignment. */ | ||||
data = malloc(size, M_ELI, M_WAITOK); | if (!g_eli_alloc_data(bp, size)) { | ||||
bp->bio_driver2 = data; | G_ELI_LOGREQ(0, bp, "Crypto request failed (ENOMEM)"); | ||||
if (bp->bio_driver1 != NULL) { | |||||
g_destroy_bio(bp->bio_driver1); | |||||
bp->bio_driver1 = NULL; | |||||
} | |||||
bp->bio_error = ENOMEM; | |||||
g_io_deliver(bp, bp->bio_error); | |||||
if (sc != NULL) | |||||
atomic_subtract_int(&sc->sc_inflight, 1); | |||||
return; | |||||
} | |||||
data = bp->bio_driver2; | |||||
p = data + encr_secsize * nsec; | p = data + encr_secsize * nsec; | ||||
} | } | ||||
bp->bio_inbed = 0; | bp->bio_inbed = 0; | ||||
bp->bio_children = nsec; | bp->bio_children = nsec; | ||||
#if defined(__mips_n64) || defined(__mips_o64) | #if defined(__mips_n64) || defined(__mips_o64) | ||||
p = (char *)roundup((uintptr_t)p, sizeof(uintptr_t)); | p = (char *)roundup((uintptr_t)p, sizeof(uintptr_t)); | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 78 Lines • Show Last 20 Lines |
I really worry that while this may be ok for swap to get ENOMEM errors, it will not be ok for a GELI disk holding real data. UFS is rather notorious for not handling EIO errors from disks well, and even the fixes that have gone in there are not meant to handle temporary errors but to instead degrade to a permanent read-only mode discarding any in-flight data AFAIK. I think if there are use cases where it might be ok for a disk to be lossy, it might need to be something that is opted into (e.g. an argument to geli(8)'s "attach" or "onetime" commands) that is per-disk and not on by default.