DF-2062 / fix.diff
diff --git a/sys/dev/disk/dm/dm_table.c b/sys/dev/disk/dm/dm_table.c --- a/sys/dev/disk/dm/dm_table.c +++ b/sys/dev/disk/dm/dm_table.c @@ -56,13 +56,13 @@ * DM_TABLE_INACTIVE will return inactive table id. */ static int -dm_table_busy(dm_table_head_t *head, uint8_t table_id) +dm_table_busy(dm_table_head_t *head, uint8_t table_id, u_int mode) { uint8_t id; id = 0; - lockmgr(&head->table_mtx, LK_SHARED); + lockmgr(&head->table_mtx, mode); if (table_id == DM_TABLE_ACTIVE) id = head->cur_active_table; @@ -95,7 +95,24 @@ { uint8_t id; - id = dm_table_busy(head, table_id); + id = dm_table_busy(head, table_id, LK_SHARED); + + return &head->tables[id]; +} + +/* + * Exclusive variant for callers that MUTATE the table list + * (e.g. dm_table_load_ioctl inserts entries into the inactive TAILQ). + * Taking LK_EXCLUSIVE serializes list mutation and prevents the + * non-atomic TAILQ_INSERT_TAIL pointer dance from racing against a + * concurrent loader/walker. + */ +dm_table_t * +dm_table_get_entry_lock(dm_table_head_t *head, uint8_t table_id) +{ + uint8_t id; + + id = dm_table_busy(head, table_id, LK_EXCLUSIVE); return &head->tables[id]; } diff --git a/sys/dev/disk/dm/dm.h b/sys/dev/disk/dm/dm.h --- a/sys/dev/disk/dm/dm.h +++ b/sys/dev/disk/dm/dm.h @@ -217,6 +217,7 @@ /* dm_table.c */ dm_table_t *dm_table_get_entry(dm_table_head_t *, uint8_t); +dm_table_t *dm_table_get_entry_lock(dm_table_head_t *, uint8_t); void dm_table_release(dm_table_head_t *, uint8_t s); void dm_table_switch_tables(dm_table_head_t *); int dm_table_destroy(dm_table_head_t *, uint8_t); diff --git a/sys/dev/disk/dm/dm_ioctl.c b/sys/dev/disk/dm/dm_ioctl.c --- a/sys/dev/disk/dm/dm_ioctl.c +++ b/sys/dev/disk/dm/dm_ioctl.c @@ -723,7 +723,7 @@ dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); dm_dbg_print_flags(dmv->flags); - tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE); + tbl = dm_table_get_entry_lock(&dmv->table_head, DM_TABLE_INACTIVE); prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); |