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