aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/net')
-rw-r--r--drivers/s390/net/Kconfig10
-rw-r--r--drivers/s390/net/Makefile1
-rw-r--r--drivers/s390/net/smsgiucv.c2
-rw-r--r--drivers/s390/net/smsgiucv.h2
-rw-r--r--drivers/s390/net/smsgiucv_app.c211
5 files changed, 225 insertions, 1 deletions
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig
index cb909a5b5047..977bb4d4ed15 100644
--- a/drivers/s390/net/Kconfig
+++ b/drivers/s390/net/Kconfig
@@ -43,6 +43,16 @@ config SMSGIUCV
43 Select this option if you want to be able to receive SMSG messages 43 Select this option if you want to be able to receive SMSG messages
44 from other VM guest systems. 44 from other VM guest systems.
45 45
46config SMSGIUCV_EVENT
47 tristate "Deliver IUCV special messages as uevents (VM only)"
48 depends on SMSGIUCV
49 help
50 Select this option to deliver CP special messages (SMSGs) as
51 uevents. The driver handles only those special messages that
52 start with "APP".
53
54 To compile as a module, choose M. The module name is "smsgiucv_app".
55
46config CLAW 56config CLAW
47 tristate "CLAW device support" 57 tristate "CLAW device support"
48 depends on CCW && NETDEVICES 58 depends on CCW && NETDEVICES
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
index 6cab5a62f99e..4dfe8c1092da 100644
--- a/drivers/s390/net/Makefile
+++ b/drivers/s390/net/Makefile
@@ -6,6 +6,7 @@ ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o
6obj-$(CONFIG_CTCM) += ctcm.o fsm.o 6obj-$(CONFIG_CTCM) += ctcm.o fsm.o
7obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o 7obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
8obj-$(CONFIG_SMSGIUCV) += smsgiucv.o 8obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
9obj-$(CONFIG_SMSGIUCV_EVENT) += smsgiucv_app.o
9obj-$(CONFIG_LCS) += lcs.o 10obj-$(CONFIG_LCS) += lcs.o
10obj-$(CONFIG_CLAW) += claw.o 11obj-$(CONFIG_CLAW) += claw.o
11qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o 12qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 3e666b6e77b4..ecef1edee701 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -177,7 +177,7 @@ static const struct dev_pm_ops smsg_pm_ops = {
177 177
178static struct device_driver smsg_driver = { 178static struct device_driver smsg_driver = {
179 .owner = THIS_MODULE, 179 .owner = THIS_MODULE,
180 .name = "SMSGIUCV", 180 .name = SMSGIUCV_DRV_NAME,
181 .bus = &iucv_bus, 181 .bus = &iucv_bus,
182 .pm = &smsg_pm_ops, 182 .pm = &smsg_pm_ops,
183}; 183};
diff --git a/drivers/s390/net/smsgiucv.h b/drivers/s390/net/smsgiucv.h
index 9410b45fde03..149a1151608d 100644
--- a/drivers/s390/net/smsgiucv.h
+++ b/drivers/s390/net/smsgiucv.h
@@ -5,6 +5,8 @@
5 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 5 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
6 */ 6 */
7 7
8#define SMSGIUCV_DRV_NAME "SMSGIUCV"
9
8int smsg_register_callback(const char *, 10int smsg_register_callback(const char *,
9 void (*)(const char *, char *)); 11 void (*)(const char *, char *));
10void smsg_unregister_callback(const char *, 12void smsg_unregister_callback(const char *,
diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c
new file mode 100644
index 000000000000..91579dc6a2b0
--- /dev/null
+++ b/drivers/s390/net/smsgiucv_app.c
@@ -0,0 +1,211 @@
1/*
2 * Deliver z/VM CP special messages (SMSG) as uevents.
3 *
4 * The driver registers for z/VM CP special messages with the
5 * "APP" prefix. Incoming messages are delivered to user space
6 * as uevents.
7 *
8 * Copyright IBM Corp. 2010
9 * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
10 *
11 */
12#define KMSG_COMPONENT "smsgiucv_app"
13#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
14
15#include <linux/ctype.h>
16#include <linux/err.h>
17#include <linux/device.h>
18#include <linux/list.h>
19#include <linux/kobject.h>
20#include <linux/module.h>
21#include <linux/spinlock.h>
22#include <linux/workqueue.h>
23#include <net/iucv/iucv.h>
24#include "smsgiucv.h"
25
26/* prefix used for SMSG registration */
27#define SMSG_PREFIX "APP"
28
29/* SMSG related uevent environment variables */
30#define ENV_SENDER_STR "SMSG_SENDER="
31#define ENV_SENDER_LEN (strlen(ENV_SENDER_STR) + 8 + 1)
32#define ENV_PREFIX_STR "SMSG_ID="
33#define ENV_PREFIX_LEN (strlen(ENV_PREFIX_STR) + \
34 strlen(SMSG_PREFIX) + 1)
35#define ENV_TEXT_STR "SMSG_TEXT="
36#define ENV_TEXT_LEN(msg) (strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
37
38/* z/VM user ID which is permitted to send SMSGs
39 * If the value is undefined or empty (""), special messages are
40 * accepted from any z/VM user ID. */
41static char *sender;
42module_param(sender, charp, 0400);
43MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
44
45/* SMSG device representation */
46static struct device *smsg_app_dev;
47
48/* list element for queuing received messages for delivery */
49struct smsg_app_event {
50 struct list_head list;
51 char *buf;
52 char *envp[4];
53};
54
55/* queue for outgoing uevents */
56static LIST_HEAD(smsg_event_queue);
57static DEFINE_SPINLOCK(smsg_event_queue_lock);
58
59static void smsg_app_event_free(struct smsg_app_event *ev)
60{
61 kfree(ev->buf);
62 kfree(ev);
63}
64
65static struct smsg_app_event *smsg_app_event_alloc(const char *from,
66 const char *msg)
67{
68 struct smsg_app_event *ev;
69
70 ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
71 if (!ev)
72 return NULL;
73
74 ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
75 ENV_TEXT_LEN(msg), GFP_ATOMIC);
76 if (!ev->buf) {
77 kfree(ev);
78 return NULL;
79 }
80
81 /* setting up environment pointers into buf */
82 ev->envp[0] = ev->buf;
83 ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
84 ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
85 ev->envp[3] = NULL;
86
87 /* setting up environment: sender, prefix name, and message text */
88 snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
89 snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
90 snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
91
92 return ev;
93}
94
95static void smsg_event_work_fn(struct work_struct *work)
96{
97 LIST_HEAD(event_queue);
98 struct smsg_app_event *p, *n;
99 struct device *dev;
100
101 dev = get_device(smsg_app_dev);
102 if (!dev)
103 return;
104
105 spin_lock_bh(&smsg_event_queue_lock);
106 list_splice_init(&smsg_event_queue, &event_queue);
107 spin_unlock_bh(&smsg_event_queue_lock);
108
109 list_for_each_entry_safe(p, n, &event_queue, list) {
110 list_del(&p->list);
111 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
112 smsg_app_event_free(p);
113 }
114
115 put_device(dev);
116}
117static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
118
119static void smsg_app_callback(const char *from, char *msg)
120{
121 struct smsg_app_event *se;
122
123 /* check if the originating z/VM user ID matches
124 * the configured sender. */
125 if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
126 return;
127
128 /* get start of message text (skip prefix and leading blanks) */
129 msg += strlen(SMSG_PREFIX);
130 while (*msg && isspace(*msg))
131 msg++;
132 if (*msg == '\0')
133 return;
134
135 /* allocate event list element and its environment */
136 se = smsg_app_event_alloc(from, msg);
137 if (!se)
138 return;
139
140 /* queue event and schedule work function */
141 spin_lock(&smsg_event_queue_lock);
142 list_add_tail(&se->list, &smsg_event_queue);
143 spin_unlock(&smsg_event_queue_lock);
144
145 schedule_work(&smsg_event_work);
146 return;
147}
148
149static int __init smsgiucv_app_init(void)
150{
151 struct device_driver *smsgiucv_drv;
152 int rc;
153
154 if (!MACHINE_IS_VM)
155 return -ENODEV;
156
157 smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
158 if (!smsg_app_dev)
159 return -ENOMEM;
160
161 smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
162 if (!smsgiucv_drv) {
163 kfree(smsg_app_dev);
164 return -ENODEV;
165 }
166
167 rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
168 if (rc) {
169 kfree(smsg_app_dev);
170 goto fail_put_driver;
171 }
172 smsg_app_dev->bus = &iucv_bus;
173 smsg_app_dev->parent = iucv_root;
174 smsg_app_dev->release = (void (*)(struct device *)) kfree;
175 smsg_app_dev->driver = smsgiucv_drv;
176 rc = device_register(smsg_app_dev);
177 if (rc) {
178 put_device(smsg_app_dev);
179 goto fail_put_driver;
180 }
181
182 /* register with the smsgiucv device driver */
183 rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
184 if (rc) {
185 device_unregister(smsg_app_dev);
186 goto fail_put_driver;
187 }
188
189 rc = 0;
190fail_put_driver:
191 put_driver(smsgiucv_drv);
192 return rc;
193}
194module_init(smsgiucv_app_init);
195
196static void __exit smsgiucv_app_exit(void)
197{
198 /* unregister callback */
199 smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
200
201 /* cancel pending work and flush any queued event work */
202 cancel_work_sync(&smsg_event_work);
203 smsg_event_work_fn(&smsg_event_work);
204
205 device_unregister(smsg_app_dev);
206}
207module_exit(smsgiucv_app_exit);
208
209MODULE_LICENSE("GPL v2");
210MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
211MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");