From: Geremy Condra With this change dm-verity errors will cause uevents to be sent to userspace, notifying it that an error has occurred and potentially triggering recovery actions. Signed-off-by: Geremy Condra Acked-by: Will Drewry FIXME AGK - Mid-edit --- drivers/md/dm-uevent.c | 116 ++++++++++++++++++++++++++++++++++++++++--------- drivers/md/dm-uevent.h | 10 +++- drivers/md/dm-verity.c | 48 ++++++++++++++++++++ 3 files changed, 151 insertions(+), 23 deletions(-) Index: linux/drivers/md/dm-uevent.c =================================================================== --- linux.orig/drivers/md/dm-uevent.c +++ linux/drivers/md/dm-uevent.c @@ -18,15 +18,14 @@ * Copyright IBM Corporation, 2007 * Author: Mike Anderson */ -#include +#include "dm.h" +#include "dm-uevent.h" + #include #include #include #include -#include "dm.h" -#include "dm-uevent.h" - #define DM_MSG_PREFIX "uevent" static const struct { @@ -36,6 +35,8 @@ static const struct { } _dm_uevent_type_names[] = { {DM_UEVENT_PATH_FAILED, KOBJ_CHANGE, "PATH_FAILED"}, {DM_UEVENT_PATH_REINSTATED, KOBJ_CHANGE, "PATH_REINSTATED"}, + {DM_UEVENT_DATA_ERROR, KOBJ_CHANGE, "DATA_ERROR"}, + {DM_UEVENT_HASH_ERROR, KOBJ_CHANGE, "HASH_ERROR"}, }; static struct kmem_cache *_dm_event_cache; @@ -68,39 +69,51 @@ static struct dm_uevent *dm_uevent_alloc return event; } -static struct dm_uevent *dm_build_path_uevent(struct mapped_device *md, - struct dm_target *ti, - enum kobject_action action, - const char *dm_action, - const char *path, - unsigned nr_valid_paths) +static int uevent_init(struct dm_uevent *event, struct mapped_device *md, + struct dm_target *ti, enum kobject_action action, + const char *dm_action) { - struct dm_uevent *event; - - event = dm_uevent_alloc(md); - if (!event) { - DMERR("%s: dm_uevent_alloc() failed", __func__); - goto err_nomem; - } - event->action = action; if (add_uevent_var(&event->ku_env, "DM_TARGET=%s", ti->type->name)) { DMERR("%s: add_uevent_var() for DM_TARGET failed", __func__); - goto err_add; + return -ENOMEM; } if (add_uevent_var(&event->ku_env, "DM_ACTION=%s", dm_action)) { DMERR("%s: add_uevent_var() for DM_ACTION failed", __func__); - goto err_add; + return -ENOMEM; } if (add_uevent_var(&event->ku_env, "DM_SEQNUM=%u", dm_next_uevent_seq(md))) { DMERR("%s: add_uevent_var() for DM_SEQNUM failed", __func__); + return -ENOMEM; + } + + return 0; +} + +static struct dm_uevent *dm_build_path_uevent(struct mapped_device *md, + struct dm_target *ti, + enum kobject_action action, + const char *dm_action, + const char *path, + unsigned nr_valid_paths) +{ + struct dm_uevent *event; + + event = dm_uevent_alloc(md); + if (!event) { + DMERR("%s: dm_uevent_alloc() failed", __func__); + goto err_nomem; + } + + if (!uevent_init(event, md, ti, action, dm_action)) { + DMERR("%s: uevent_init() failed", __func__); goto err_add; } @@ -124,6 +137,36 @@ err_nomem: return ERR_PTR(-ENOMEM); } +static struct dm_uevent *dm_build_verity_uevent(struct mapped_device *md, + struct dm_target *ti, + enum kobject_action action, + const char *dm_action, + sector_t block_nr) +{ + struct dm_uevent *event; + + event = dm_uevent_alloc(md); + if (!event) { + DMERR("%s: dm_uevent_alloc() failed", __func__); + goto err_nomem; + } + + if (!uevent_init(event, md, ti, action, dm_action)) { + DMERR("%s: uevent_init() failed", __func__); + goto err_add; + } + + if (add_uevent_var(&event->ku_env, "DM_VERITY_BLOCK_NR=%llu", (unsigned long long)block_nr)) { + DMERR("%s: add_uevent_var() for DM_VERITY_BLOCK failed", __func__); + goto err_add; + } + +err_add: + dm_uevent_free(event); +err_nomem: + return ERR_PTR(-ENOMEM); +} + /** * dm_send_uevents - send uevents for given list * @@ -202,13 +245,44 @@ void dm_path_uevent(enum dm_uevent_type } EXPORT_SYMBOL_GPL(dm_path_uevent); +/** + * dm_verity_uevent - called to create a new verity event and queue it + * + * @event_type: verity event type enum + * @ti: pointer to a dm_target + * @block_nr: block number triggering the event + * + */ +void dm_verity_uevent(enum dm_uevent_type event_type, struct dm_target *ti, + sector_t block_nr) +{ + struct mapped_device *md = dm_table_get_md(ti->table); + struct dm_uevent *event; + + if (event_type >= ARRAY_SIZE(_dm_uevent_type_names)) { + DMERR("%s: Invalid event_type %d", __func__, event_type); + return; + } + + event = dm_build_verity_uevent(md, ti, + _dm_uevent_type_names[event_type].action, + _dm_uevent_type_names[event_type].name, + block_nr); + + if (IS_ERR(event)) + return; + + dm_uevent_add(md, &event->elist); +} +EXPORT_SYMBOL_GPL(dm_verity_uevent); + int dm_uevent_init(void) { _dm_event_cache = KMEM_CACHE(dm_uevent, 0); if (!_dm_event_cache) return -ENOMEM; - DMINFO("version 1.0.3"); + DMINFO("version 1.1.0"); return 0; } Index: linux/drivers/md/dm-uevent.h =================================================================== --- linux.orig/drivers/md/dm-uevent.h +++ linux/drivers/md/dm-uevent.h @@ -24,6 +24,8 @@ enum dm_uevent_type { DM_UEVENT_PATH_FAILED, DM_UEVENT_PATH_REINSTATED, + DM_UEVENT_DATA_ERROR, + DM_UEVENT_HASH_ERROR, }; #ifdef CONFIG_DM_UEVENT @@ -34,7 +36,8 @@ extern void dm_send_uevents(struct list_ extern void dm_path_uevent(enum dm_uevent_type event_type, struct dm_target *ti, const char *path, unsigned nr_valid_paths); - +extern void dm_verity_uevent(enum dm_uevent_type event_type, + struct dm_target *ti, sector_t block_nr); #else static inline int dm_uevent_init(void) @@ -53,7 +56,10 @@ static inline void dm_path_uevent(enum d unsigned nr_valid_paths) { } - +static inline void dm_verity_uevent(enum dm_uevent_type event_type, + struct dm_target *ti, sector_t block_nr) +{ +} #endif /* CONFIG_DM_UEVENT */ #endif /* DM_UEVENT_H */ Index: linux/drivers/md/dm-verity.c =================================================================== --- linux.orig/drivers/md/dm-verity.c +++ linux/drivers/md/dm-verity.c @@ -20,6 +20,8 @@ #include #include +#include "dm-uevent.h" + #define DM_MSG_PREFIX "verity" #define DM_VERITY_IO_VEC_INLINE 16 @@ -58,6 +60,7 @@ struct dm_verity { mempool_t *vec_mempool; /* mempool of bio vector */ struct workqueue_struct *verify_wq; + struct work_struct trigger_event; /* starting blocks for each tree level. 0 is the lowest level. */ sector_t hash_level_block[DM_VERITY_MAX_LEVELS]; @@ -142,6 +145,44 @@ static void dm_bufio_alloc_callback(stru } /* + * Trigger userspace data corruption handler. + */ +static void trigger_event(struct work_struct *work) +{ + struct dm_verity *v = container_of(work, struct dm_verity, trigger_event); + + dm_table_event(v->ti->table); +} + +static void verity_postsuspend(struct dm_target *ti) +{ + struct dm_verity *v = ti->private; + + flush_work(&v->trigger_event); +} + +#ifdef CONFIG_DM_UEVENT +static void verity_data_error(struct dm_verity *v, unsigned long long block_nr) +{ + dm_verity_uevent(DM_UEVENT_DATA_ERROR, v->ti, block_nr); + schedule_work(&v->trigger_event); +} + +static void verity_hash_error(struct dm_verity *v, unsigned long long block_nr) +{ + dm_verity_uevent(DM_UEVENT_HASH_ERROR, v->ti, block_nr); + schedule_work(&v->trigger_event); +} +#else +static inline void verity_data_error(struct dm_verity *v, unsigned long long block_nr) +{ +} +static inline void verity_hash_error(struct dm_verity *v, unsigned long long block_nr) +{ +} +#endif + +/* * Translate input sector number to the sector number on the target device. */ static sector_t verity_map_sector(struct dm_verity *v, sector_t bi_sector) @@ -260,6 +301,7 @@ static int verity_verify_level(struct dm (unsigned long long)hash_block); v->hash_failed = 1; r = -EIO; + verity_hash_error(v, (unsigned long long)hash_block); goto release_ret_r; } else aux->hash_verified = 1; @@ -380,6 +422,7 @@ test_block_hash: DMERR_LIMIT("data block %llu is corrupted", (unsigned long long)(io->block + b)); v->hash_failed = 1; + verity_data_error(v, (unsigned long long)(io->block + b)); return -EIO; } } @@ -625,6 +668,8 @@ static void verity_dtr(struct dm_target { struct dm_verity *v = ti->private; + flush_work(&v->trigger_event); + if (v->verify_wq) destroy_workqueue(v->verify_wq); @@ -876,6 +921,8 @@ static int verity_ctr(struct dm_target * goto bad; } + INIT_WORK(&v->trigger_event, trigger_event); + return 0; bad: @@ -894,6 +941,7 @@ static struct target_type verity_target .status = verity_status, .ioctl = verity_ioctl, .merge = verity_merge, + .postsuspend = verity_postsuspend, .iterate_devices = verity_iterate_devices, .io_hints = verity_io_hints, };