DF-2453 / fix.diff
diff --git a/sys/dev/disk/dm/flakey/dm_target_flakey.c b/sys/dev/disk/dm/flakey/dm_target_flakey.c --- a/sys/dev/disk/dm/flakey/dm_target_flakey.c +++ b/sys/dev/disk/dm/flakey/dm_target_flakey.c @@ -34,11 +34,14 @@ #include <sys/param.h> #include <sys/malloc.h> +#include <cpu/atomic.h> #include <dev/disk/dm/dm.h> MALLOC_DEFINE(M_DMFLAKEY, "dm_flakey", "Device Mapper Target Flakey"); -/* dm_flakey never updates any field after initialization */ +/* dm_flakey never updates any field after initialization. + * ref_cnt protects tfc from being freed while async read I/O + * (which stores tfc in bio_caller_info1) is in flight. */ typedef struct target_flakey_config { dm_pdev_t *pdev; uint64_t offset; @@ -54,11 +57,32 @@ unsigned int corrupt_buf_rw; unsigned int corrupt_buf_value; unsigned int corrupt_buf_flags; /* for B_XXX flags */ + int ref_cnt; /* refcount: 1 for table + 1 per in-flight async read */ } dm_target_flakey_config_t; #define FLAKEY_CORRUPT_DIR(tfc) \ ((tfc)->corrupt_buf_rw == BUF_CMD_READ ? 'r' : 'w') +static __inline void +tfc_hold(dm_target_flakey_config_t *tfc) +{ + atomic_add_int(&tfc->ref_cnt, 1); +} + +/* + * Drop a reference on tfc. When the last reference is released + * (either the table destroy or the last in-flight async read iodone), + * free the config and its pdev reference. + */ +static __inline void +tfc_release(dm_target_flakey_config_t *tfc) +{ + if (atomic_fetchadd_int(&tfc->ref_cnt, -1) == 1) { + dm_pdev_decr(tfc->pdev); + kfree(tfc, M_DMFLAKEY); + } +} + static int _init_features(dm_target_flakey_config_t*, int, char**); static __inline void _submit(dm_target_flakey_config_t*, struct bio*); static int _flakey_read(dm_target_flakey_config_t*, struct buf*); @@ -82,6 +106,7 @@ tfc = kmalloc(sizeof(*tfc), M_DMFLAKEY, M_WAITOK | M_ZERO); if (tfc == NULL) return ENOMEM; + tfc->ref_cnt = 1; /* initial reference for the table entry */ if ((dmp = dm_pdev_insert(argv[0])) == NULL) { err = ENOENT; @@ -214,9 +239,7 @@ if (tfc == NULL) return 0; - dm_pdev_decr(tfc->pdev); - - kfree(tfc, M_DMFLAKEY); + tfc_release(tfc); return 0; } @@ -282,6 +305,8 @@ _flakey_eio_buf(bio->bio_buf); biodone(obio); + + tfc_release(tfc); /* drop ref acquired in _flakey_read */ } static int @@ -302,6 +327,7 @@ nbio = push_bio(bio); nbio->bio_done = _flakey_read_iodone; + tfc_hold(tfc); /* released in _flakey_read_iodone */ nbio->bio_caller_info1.ptr = tfc; nbio->bio_offset = pop_bio(nbio)->bio_offset; |