xref: /OK3568_Linux_fs/kernel/drivers/s390/net/smsgiucv_app.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Deliver z/VM CP special messages (SMSG) as uevents.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * The driver registers for z/VM CP special messages with the
6*4882a593Smuzhiyun  * "APP" prefix. Incoming messages are delivered to user space
7*4882a593Smuzhiyun  * as uevents.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Copyright IBM Corp. 2010
10*4882a593Smuzhiyun  * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun #define KMSG_COMPONENT		"smsgiucv_app"
14*4882a593Smuzhiyun #define pr_fmt(fmt)		KMSG_COMPONENT ": " fmt
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <linux/ctype.h>
17*4882a593Smuzhiyun #include <linux/err.h>
18*4882a593Smuzhiyun #include <linux/device.h>
19*4882a593Smuzhiyun #include <linux/list.h>
20*4882a593Smuzhiyun #include <linux/kobject.h>
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/spinlock.h>
24*4882a593Smuzhiyun #include <linux/workqueue.h>
25*4882a593Smuzhiyun #include <net/iucv/iucv.h>
26*4882a593Smuzhiyun #include "smsgiucv.h"
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /* prefix used for SMSG registration */
29*4882a593Smuzhiyun #define SMSG_PREFIX		"APP"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun /* SMSG related uevent environment variables */
32*4882a593Smuzhiyun #define ENV_SENDER_STR		"SMSG_SENDER="
33*4882a593Smuzhiyun #define ENV_SENDER_LEN		(strlen(ENV_SENDER_STR) + 8 + 1)
34*4882a593Smuzhiyun #define ENV_PREFIX_STR		"SMSG_ID="
35*4882a593Smuzhiyun #define ENV_PREFIX_LEN		(strlen(ENV_PREFIX_STR) + \
36*4882a593Smuzhiyun 				 strlen(SMSG_PREFIX) + 1)
37*4882a593Smuzhiyun #define ENV_TEXT_STR		"SMSG_TEXT="
38*4882a593Smuzhiyun #define ENV_TEXT_LEN(msg)	(strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun /* z/VM user ID which is permitted to send SMSGs
41*4882a593Smuzhiyun  * If the value is undefined or empty (""), special messages are
42*4882a593Smuzhiyun  * accepted from any z/VM user ID. */
43*4882a593Smuzhiyun static char *sender;
44*4882a593Smuzhiyun module_param(sender, charp, 0400);
45*4882a593Smuzhiyun MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun /* SMSG device representation */
48*4882a593Smuzhiyun static struct device *smsg_app_dev;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /* list element for queuing received messages for delivery */
51*4882a593Smuzhiyun struct smsg_app_event {
52*4882a593Smuzhiyun 	struct list_head list;
53*4882a593Smuzhiyun 	char *buf;
54*4882a593Smuzhiyun 	char *envp[4];
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun /* queue for outgoing uevents */
58*4882a593Smuzhiyun static LIST_HEAD(smsg_event_queue);
59*4882a593Smuzhiyun static DEFINE_SPINLOCK(smsg_event_queue_lock);
60*4882a593Smuzhiyun 
smsg_app_event_free(struct smsg_app_event * ev)61*4882a593Smuzhiyun static void smsg_app_event_free(struct smsg_app_event *ev)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	kfree(ev->buf);
64*4882a593Smuzhiyun 	kfree(ev);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
smsg_app_event_alloc(const char * from,const char * msg)67*4882a593Smuzhiyun static struct smsg_app_event *smsg_app_event_alloc(const char *from,
68*4882a593Smuzhiyun 						   const char *msg)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	struct smsg_app_event *ev;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
73*4882a593Smuzhiyun 	if (!ev)
74*4882a593Smuzhiyun 		return NULL;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
77*4882a593Smuzhiyun 			  ENV_TEXT_LEN(msg), GFP_ATOMIC);
78*4882a593Smuzhiyun 	if (!ev->buf) {
79*4882a593Smuzhiyun 		kfree(ev);
80*4882a593Smuzhiyun 		return NULL;
81*4882a593Smuzhiyun 	}
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	/* setting up environment pointers into buf */
84*4882a593Smuzhiyun 	ev->envp[0] = ev->buf;
85*4882a593Smuzhiyun 	ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
86*4882a593Smuzhiyun 	ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
87*4882a593Smuzhiyun 	ev->envp[3] = NULL;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	/* setting up environment: sender, prefix name, and message text */
90*4882a593Smuzhiyun 	snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
91*4882a593Smuzhiyun 	snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
92*4882a593Smuzhiyun 	snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	return ev;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
smsg_event_work_fn(struct work_struct * work)97*4882a593Smuzhiyun static void smsg_event_work_fn(struct work_struct *work)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	LIST_HEAD(event_queue);
100*4882a593Smuzhiyun 	struct smsg_app_event *p, *n;
101*4882a593Smuzhiyun 	struct device *dev;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	dev = get_device(smsg_app_dev);
104*4882a593Smuzhiyun 	if (!dev)
105*4882a593Smuzhiyun 		return;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	spin_lock_bh(&smsg_event_queue_lock);
108*4882a593Smuzhiyun 	list_splice_init(&smsg_event_queue, &event_queue);
109*4882a593Smuzhiyun 	spin_unlock_bh(&smsg_event_queue_lock);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	list_for_each_entry_safe(p, n, &event_queue, list) {
112*4882a593Smuzhiyun 		list_del(&p->list);
113*4882a593Smuzhiyun 		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
114*4882a593Smuzhiyun 		smsg_app_event_free(p);
115*4882a593Smuzhiyun 	}
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	put_device(dev);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
120*4882a593Smuzhiyun 
smsg_app_callback(const char * from,char * msg)121*4882a593Smuzhiyun static void smsg_app_callback(const char *from, char *msg)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	struct smsg_app_event *se;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	/* check if the originating z/VM user ID matches
126*4882a593Smuzhiyun 	 * the configured sender. */
127*4882a593Smuzhiyun 	if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
128*4882a593Smuzhiyun 		return;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	/* get start of message text (skip prefix and leading blanks) */
131*4882a593Smuzhiyun 	msg += strlen(SMSG_PREFIX);
132*4882a593Smuzhiyun 	while (*msg && isspace(*msg))
133*4882a593Smuzhiyun 		msg++;
134*4882a593Smuzhiyun 	if (*msg == '\0')
135*4882a593Smuzhiyun 		return;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	/* allocate event list element and its environment */
138*4882a593Smuzhiyun 	se = smsg_app_event_alloc(from, msg);
139*4882a593Smuzhiyun 	if (!se)
140*4882a593Smuzhiyun 		return;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	/* queue event and schedule work function */
143*4882a593Smuzhiyun 	spin_lock(&smsg_event_queue_lock);
144*4882a593Smuzhiyun 	list_add_tail(&se->list, &smsg_event_queue);
145*4882a593Smuzhiyun 	spin_unlock(&smsg_event_queue_lock);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	schedule_work(&smsg_event_work);
148*4882a593Smuzhiyun 	return;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
smsgiucv_app_init(void)151*4882a593Smuzhiyun static int __init smsgiucv_app_init(void)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct device_driver *smsgiucv_drv;
154*4882a593Smuzhiyun 	int rc;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	if (!MACHINE_IS_VM)
157*4882a593Smuzhiyun 		return -ENODEV;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
160*4882a593Smuzhiyun 	if (!smsg_app_dev)
161*4882a593Smuzhiyun 		return -ENOMEM;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
164*4882a593Smuzhiyun 	if (!smsgiucv_drv) {
165*4882a593Smuzhiyun 		kfree(smsg_app_dev);
166*4882a593Smuzhiyun 		return -ENODEV;
167*4882a593Smuzhiyun 	}
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
170*4882a593Smuzhiyun 	if (rc) {
171*4882a593Smuzhiyun 		kfree(smsg_app_dev);
172*4882a593Smuzhiyun 		goto fail;
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 	smsg_app_dev->bus = &iucv_bus;
175*4882a593Smuzhiyun 	smsg_app_dev->parent = iucv_root;
176*4882a593Smuzhiyun 	smsg_app_dev->release = (void (*)(struct device *)) kfree;
177*4882a593Smuzhiyun 	smsg_app_dev->driver = smsgiucv_drv;
178*4882a593Smuzhiyun 	rc = device_register(smsg_app_dev);
179*4882a593Smuzhiyun 	if (rc) {
180*4882a593Smuzhiyun 		put_device(smsg_app_dev);
181*4882a593Smuzhiyun 		goto fail;
182*4882a593Smuzhiyun 	}
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	/* convert sender to uppercase characters */
185*4882a593Smuzhiyun 	if (sender) {
186*4882a593Smuzhiyun 		int len = strlen(sender);
187*4882a593Smuzhiyun 		while (len--)
188*4882a593Smuzhiyun 			sender[len] = toupper(sender[len]);
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	/* register with the smsgiucv device driver */
192*4882a593Smuzhiyun 	rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
193*4882a593Smuzhiyun 	if (rc) {
194*4882a593Smuzhiyun 		device_unregister(smsg_app_dev);
195*4882a593Smuzhiyun 		goto fail;
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	rc = 0;
199*4882a593Smuzhiyun fail:
200*4882a593Smuzhiyun 	return rc;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun module_init(smsgiucv_app_init);
203*4882a593Smuzhiyun 
smsgiucv_app_exit(void)204*4882a593Smuzhiyun static void __exit smsgiucv_app_exit(void)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun 	/* unregister callback */
207*4882a593Smuzhiyun 	smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/* cancel pending work and flush any queued event work */
210*4882a593Smuzhiyun 	cancel_work_sync(&smsg_event_work);
211*4882a593Smuzhiyun 	smsg_event_work_fn(&smsg_event_work);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	device_unregister(smsg_app_dev);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun module_exit(smsgiucv_app_exit);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
218*4882a593Smuzhiyun MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
219*4882a593Smuzhiyun MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");
220