18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Device Mapper Uevent Support (dm-uevent)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corporation, 2007
68c2ecf20Sopenharmony_ci * 	Author: Mike Anderson <andmike@linux.vnet.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/list.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/kobject.h>
118c2ecf20Sopenharmony_ci#include <linux/dm-ioctl.h>
128c2ecf20Sopenharmony_ci#include <linux/export.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "dm.h"
158c2ecf20Sopenharmony_ci#include "dm-uevent.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "uevent"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic const struct {
208c2ecf20Sopenharmony_ci	enum dm_uevent_type type;
218c2ecf20Sopenharmony_ci	enum kobject_action action;
228c2ecf20Sopenharmony_ci	char *name;
238c2ecf20Sopenharmony_ci} _dm_uevent_type_names[] = {
248c2ecf20Sopenharmony_ci	{DM_UEVENT_PATH_FAILED, KOBJ_CHANGE, "PATH_FAILED"},
258c2ecf20Sopenharmony_ci	{DM_UEVENT_PATH_REINSTATED, KOBJ_CHANGE, "PATH_REINSTATED"},
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic struct kmem_cache *_dm_event_cache;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistruct dm_uevent {
318c2ecf20Sopenharmony_ci	struct mapped_device *md;
328c2ecf20Sopenharmony_ci	enum kobject_action action;
338c2ecf20Sopenharmony_ci	struct kobj_uevent_env ku_env;
348c2ecf20Sopenharmony_ci	struct list_head elist;
358c2ecf20Sopenharmony_ci	char name[DM_NAME_LEN];
368c2ecf20Sopenharmony_ci	char uuid[DM_UUID_LEN];
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void dm_uevent_free(struct dm_uevent *event)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	kmem_cache_free(_dm_event_cache, event);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic struct dm_uevent *dm_uevent_alloc(struct mapped_device *md)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct dm_uevent *event;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	event = kmem_cache_zalloc(_dm_event_cache, GFP_ATOMIC);
498c2ecf20Sopenharmony_ci	if (!event)
508c2ecf20Sopenharmony_ci		return NULL;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&event->elist);
538c2ecf20Sopenharmony_ci	event->md = md;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return event;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct dm_uevent *dm_build_path_uevent(struct mapped_device *md,
598c2ecf20Sopenharmony_ci					      struct dm_target *ti,
608c2ecf20Sopenharmony_ci					      enum kobject_action action,
618c2ecf20Sopenharmony_ci					      const char *dm_action,
628c2ecf20Sopenharmony_ci					      const char *path,
638c2ecf20Sopenharmony_ci					      unsigned nr_valid_paths)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct dm_uevent *event;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	event = dm_uevent_alloc(md);
688c2ecf20Sopenharmony_ci	if (!event) {
698c2ecf20Sopenharmony_ci		DMERR("%s: dm_uevent_alloc() failed", __func__);
708c2ecf20Sopenharmony_ci		goto err_nomem;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	event->action = action;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (add_uevent_var(&event->ku_env, "DM_TARGET=%s", ti->type->name)) {
768c2ecf20Sopenharmony_ci		DMERR("%s: add_uevent_var() for DM_TARGET failed",
778c2ecf20Sopenharmony_ci		      __func__);
788c2ecf20Sopenharmony_ci		goto err_add;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (add_uevent_var(&event->ku_env, "DM_ACTION=%s", dm_action)) {
828c2ecf20Sopenharmony_ci		DMERR("%s: add_uevent_var() for DM_ACTION failed",
838c2ecf20Sopenharmony_ci		      __func__);
848c2ecf20Sopenharmony_ci		goto err_add;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (add_uevent_var(&event->ku_env, "DM_SEQNUM=%u",
888c2ecf20Sopenharmony_ci			   dm_next_uevent_seq(md))) {
898c2ecf20Sopenharmony_ci		DMERR("%s: add_uevent_var() for DM_SEQNUM failed",
908c2ecf20Sopenharmony_ci		      __func__);
918c2ecf20Sopenharmony_ci		goto err_add;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (add_uevent_var(&event->ku_env, "DM_PATH=%s", path)) {
958c2ecf20Sopenharmony_ci		DMERR("%s: add_uevent_var() for DM_PATH failed", __func__);
968c2ecf20Sopenharmony_ci		goto err_add;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (add_uevent_var(&event->ku_env, "DM_NR_VALID_PATHS=%d",
1008c2ecf20Sopenharmony_ci			   nr_valid_paths)) {
1018c2ecf20Sopenharmony_ci		DMERR("%s: add_uevent_var() for DM_NR_VALID_PATHS failed",
1028c2ecf20Sopenharmony_ci		      __func__);
1038c2ecf20Sopenharmony_ci		goto err_add;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return event;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cierr_add:
1098c2ecf20Sopenharmony_ci	dm_uevent_free(event);
1108c2ecf20Sopenharmony_cierr_nomem:
1118c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/**
1158c2ecf20Sopenharmony_ci * dm_send_uevents - send uevents for given list
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * @events:	list of events to send
1188c2ecf20Sopenharmony_ci * @kobj:	kobject generating event
1198c2ecf20Sopenharmony_ci *
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_civoid dm_send_uevents(struct list_head *events, struct kobject *kobj)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int r;
1248c2ecf20Sopenharmony_ci	struct dm_uevent *event, *next;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(event, next, events, elist) {
1278c2ecf20Sopenharmony_ci		list_del_init(&event->elist);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		/*
1308c2ecf20Sopenharmony_ci		 * When a device is being removed this copy fails and we
1318c2ecf20Sopenharmony_ci		 * discard these unsent events.
1328c2ecf20Sopenharmony_ci		 */
1338c2ecf20Sopenharmony_ci		if (dm_copy_name_and_uuid(event->md, event->name,
1348c2ecf20Sopenharmony_ci					  event->uuid)) {
1358c2ecf20Sopenharmony_ci			DMINFO("%s: skipping sending uevent for lost device",
1368c2ecf20Sopenharmony_ci			       __func__);
1378c2ecf20Sopenharmony_ci			goto uevent_free;
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		if (add_uevent_var(&event->ku_env, "DM_NAME=%s", event->name)) {
1418c2ecf20Sopenharmony_ci			DMERR("%s: add_uevent_var() for DM_NAME failed",
1428c2ecf20Sopenharmony_ci			      __func__);
1438c2ecf20Sopenharmony_ci			goto uevent_free;
1448c2ecf20Sopenharmony_ci		}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		if (add_uevent_var(&event->ku_env, "DM_UUID=%s", event->uuid)) {
1478c2ecf20Sopenharmony_ci			DMERR("%s: add_uevent_var() for DM_UUID failed",
1488c2ecf20Sopenharmony_ci			      __func__);
1498c2ecf20Sopenharmony_ci			goto uevent_free;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		r = kobject_uevent_env(kobj, event->action, event->ku_env.envp);
1538c2ecf20Sopenharmony_ci		if (r)
1548c2ecf20Sopenharmony_ci			DMERR("%s: kobject_uevent_env failed", __func__);
1558c2ecf20Sopenharmony_ciuevent_free:
1568c2ecf20Sopenharmony_ci		dm_uevent_free(event);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_send_uevents);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci * dm_path_uevent - called to create a new path event and queue it
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci * @event_type:	path event type enum
1658c2ecf20Sopenharmony_ci * @ti:			pointer to a dm_target
1668c2ecf20Sopenharmony_ci * @path:		string containing pathname
1678c2ecf20Sopenharmony_ci * @nr_valid_paths:	number of valid paths remaining
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_civoid dm_path_uevent(enum dm_uevent_type event_type, struct dm_target *ti,
1718c2ecf20Sopenharmony_ci		   const char *path, unsigned nr_valid_paths)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct mapped_device *md = dm_table_get_md(ti->table);
1748c2ecf20Sopenharmony_ci	struct dm_uevent *event;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (event_type >= ARRAY_SIZE(_dm_uevent_type_names)) {
1778c2ecf20Sopenharmony_ci		DMERR("%s: Invalid event_type %d", __func__, event_type);
1788c2ecf20Sopenharmony_ci		return;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	event = dm_build_path_uevent(md, ti,
1828c2ecf20Sopenharmony_ci				     _dm_uevent_type_names[event_type].action,
1838c2ecf20Sopenharmony_ci				     _dm_uevent_type_names[event_type].name,
1848c2ecf20Sopenharmony_ci				     path, nr_valid_paths);
1858c2ecf20Sopenharmony_ci	if (IS_ERR(event))
1868c2ecf20Sopenharmony_ci		return;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	dm_uevent_add(md, &event->elist);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_path_uevent);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ciint dm_uevent_init(void)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	_dm_event_cache = KMEM_CACHE(dm_uevent, 0);
1958c2ecf20Sopenharmony_ci	if (!_dm_event_cache)
1968c2ecf20Sopenharmony_ci		return -ENOMEM;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	DMINFO("version 1.0.3");
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_civoid dm_uevent_exit(void)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	kmem_cache_destroy(_dm_event_cache);
2068c2ecf20Sopenharmony_ci}
207