diff options
Diffstat (limited to 'drivers/s390/char/monreader.c')
-rw-r--r-- | drivers/s390/char/monreader.c | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c new file mode 100644 index 000000000000..5fd3ad867386 --- /dev/null +++ b/drivers/s390/char/monreader.c | |||
@@ -0,0 +1,662 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/monreader.c | ||
3 | * | ||
4 | * Character device driver for reading z/VM *MONITOR service records. | ||
5 | * | ||
6 | * Copyright (C) 2004 IBM Corporation, IBM Deutschland Entwicklung GmbH. | ||
7 | * | ||
8 | * Author: Gerald Schaefer <geraldsc@de.ibm.com> | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/moduleparam.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/miscdevice.h> | ||
18 | #include <linux/ctype.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <asm/uaccess.h> | ||
22 | #include <asm/ebcdic.h> | ||
23 | #include <asm/extmem.h> | ||
24 | #include <linux/poll.h> | ||
25 | #include "../net/iucv.h" | ||
26 | |||
27 | |||
28 | //#define MON_DEBUG /* Debug messages on/off */ | ||
29 | |||
30 | #define MON_NAME "monreader" | ||
31 | |||
32 | #define P_INFO(x...) printk(KERN_INFO MON_NAME " info: " x) | ||
33 | #define P_ERROR(x...) printk(KERN_ERR MON_NAME " error: " x) | ||
34 | #define P_WARNING(x...) printk(KERN_WARNING MON_NAME " warning: " x) | ||
35 | |||
36 | #ifdef MON_DEBUG | ||
37 | #define P_DEBUG(x...) printk(KERN_DEBUG MON_NAME " debug: " x) | ||
38 | #else | ||
39 | #define P_DEBUG(x...) do {} while (0) | ||
40 | #endif | ||
41 | |||
42 | #define MON_COLLECT_SAMPLE 0x80 | ||
43 | #define MON_COLLECT_EVENT 0x40 | ||
44 | #define MON_SERVICE "*MONITOR" | ||
45 | #define MON_IN_USE 0x01 | ||
46 | #define MON_MSGLIM 255 | ||
47 | |||
48 | static char mon_dcss_name[9] = "MONDCSS\0"; | ||
49 | |||
50 | struct mon_msg { | ||
51 | u32 pos; | ||
52 | u32 mca_offset; | ||
53 | iucv_MessagePending local_eib; | ||
54 | char msglim_reached; | ||
55 | char replied_msglim; | ||
56 | }; | ||
57 | |||
58 | struct mon_private { | ||
59 | u16 pathid; | ||
60 | iucv_handle_t iucv_handle; | ||
61 | struct mon_msg *msg_array[MON_MSGLIM]; | ||
62 | unsigned int write_index; | ||
63 | unsigned int read_index; | ||
64 | atomic_t msglim_count; | ||
65 | atomic_t read_ready; | ||
66 | atomic_t iucv_connected; | ||
67 | atomic_t iucv_severed; | ||
68 | }; | ||
69 | |||
70 | static unsigned long mon_in_use = 0; | ||
71 | |||
72 | static unsigned long mon_dcss_start; | ||
73 | static unsigned long mon_dcss_end; | ||
74 | |||
75 | static DECLARE_WAIT_QUEUE_HEAD(mon_read_wait_queue); | ||
76 | static DECLARE_WAIT_QUEUE_HEAD(mon_conn_wait_queue); | ||
77 | |||
78 | static u8 iucv_host[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
79 | |||
80 | static u8 user_data_connect[16] = { | ||
81 | /* Version code, must be 0x01 for shared mode */ | ||
82 | 0x01, | ||
83 | /* what to collect */ | ||
84 | MON_COLLECT_SAMPLE | MON_COLLECT_EVENT, | ||
85 | /* DCSS name in EBCDIC, 8 bytes padded with blanks */ | ||
86 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
87 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
88 | }; | ||
89 | |||
90 | static u8 user_data_sever[16] = { | ||
91 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
92 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
93 | }; | ||
94 | |||
95 | |||
96 | /****************************************************************************** | ||
97 | * helper functions * | ||
98 | *****************************************************************************/ | ||
99 | /* | ||
100 | * Create the 8 bytes EBCDIC DCSS segment name from | ||
101 | * an ASCII name, incl. padding | ||
102 | */ | ||
103 | static inline void | ||
104 | dcss_mkname(char *ascii_name, char *ebcdic_name) | ||
105 | { | ||
106 | int i; | ||
107 | |||
108 | for (i = 0; i < 8; i++) { | ||
109 | if (ascii_name[i] == '\0') | ||
110 | break; | ||
111 | ebcdic_name[i] = toupper(ascii_name[i]); | ||
112 | }; | ||
113 | for (; i < 8; i++) | ||
114 | ebcdic_name[i] = ' '; | ||
115 | ASCEBC(ebcdic_name, 8); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * print appropriate error message for segment_load()/segment_type() | ||
120 | * return code | ||
121 | */ | ||
122 | static void | ||
123 | mon_segment_warn(int rc, char* seg_name) | ||
124 | { | ||
125 | switch (rc) { | ||
126 | case -ENOENT: | ||
127 | P_WARNING("cannot load/query segment %s, does not exist\n", | ||
128 | seg_name); | ||
129 | break; | ||
130 | case -ENOSYS: | ||
131 | P_WARNING("cannot load/query segment %s, not running on VM\n", | ||
132 | seg_name); | ||
133 | break; | ||
134 | case -EIO: | ||
135 | P_WARNING("cannot load/query segment %s, hardware error\n", | ||
136 | seg_name); | ||
137 | break; | ||
138 | case -ENOTSUPP: | ||
139 | P_WARNING("cannot load/query segment %s, is a multi-part " | ||
140 | "segment\n", seg_name); | ||
141 | break; | ||
142 | case -ENOSPC: | ||
143 | P_WARNING("cannot load/query segment %s, overlaps with " | ||
144 | "storage\n", seg_name); | ||
145 | break; | ||
146 | case -EBUSY: | ||
147 | P_WARNING("cannot load/query segment %s, overlaps with " | ||
148 | "already loaded dcss\n", seg_name); | ||
149 | break; | ||
150 | case -EPERM: | ||
151 | P_WARNING("cannot load/query segment %s, already loaded in " | ||
152 | "incompatible mode\n", seg_name); | ||
153 | break; | ||
154 | case -ENOMEM: | ||
155 | P_WARNING("cannot load/query segment %s, out of memory\n", | ||
156 | seg_name); | ||
157 | break; | ||
158 | case -ERANGE: | ||
159 | P_WARNING("cannot load/query segment %s, exceeds kernel " | ||
160 | "mapping range\n", seg_name); | ||
161 | break; | ||
162 | default: | ||
163 | P_WARNING("cannot load/query segment %s, return value %i\n", | ||
164 | seg_name, rc); | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | static inline unsigned long | ||
170 | mon_mca_start(struct mon_msg *monmsg) | ||
171 | { | ||
172 | return monmsg->local_eib.ln1msg1.iprmmsg1_u32; | ||
173 | } | ||
174 | |||
175 | static inline unsigned long | ||
176 | mon_mca_end(struct mon_msg *monmsg) | ||
177 | { | ||
178 | return monmsg->local_eib.ln1msg2.ipbfln1f; | ||
179 | } | ||
180 | |||
181 | static inline u8 | ||
182 | mon_mca_type(struct mon_msg *monmsg, u8 index) | ||
183 | { | ||
184 | return *((u8 *) mon_mca_start(monmsg) + monmsg->mca_offset + index); | ||
185 | } | ||
186 | |||
187 | static inline u32 | ||
188 | mon_mca_size(struct mon_msg *monmsg) | ||
189 | { | ||
190 | return mon_mca_end(monmsg) - mon_mca_start(monmsg) + 1; | ||
191 | } | ||
192 | |||
193 | static inline u32 | ||
194 | mon_rec_start(struct mon_msg *monmsg) | ||
195 | { | ||
196 | return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 4)); | ||
197 | } | ||
198 | |||
199 | static inline u32 | ||
200 | mon_rec_end(struct mon_msg *monmsg) | ||
201 | { | ||
202 | return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8)); | ||
203 | } | ||
204 | |||
205 | static inline int | ||
206 | mon_check_mca(struct mon_msg *monmsg) | ||
207 | { | ||
208 | if ((mon_rec_end(monmsg) <= mon_rec_start(monmsg)) || | ||
209 | (mon_rec_start(monmsg) < mon_dcss_start) || | ||
210 | (mon_rec_end(monmsg) > mon_dcss_end) || | ||
211 | (mon_mca_type(monmsg, 0) == 0) || | ||
212 | (mon_mca_size(monmsg) % 12 != 0) || | ||
213 | (mon_mca_end(monmsg) <= mon_mca_start(monmsg)) || | ||
214 | (mon_mca_end(monmsg) > mon_dcss_end) || | ||
215 | (mon_mca_start(monmsg) < mon_dcss_start) || | ||
216 | ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0))) | ||
217 | { | ||
218 | P_DEBUG("READ, IGNORED INVALID MCA\n\n"); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static inline int | ||
225 | mon_send_reply(struct mon_msg *monmsg, struct mon_private *monpriv) | ||
226 | { | ||
227 | u8 prmmsg[8]; | ||
228 | int rc; | ||
229 | |||
230 | P_DEBUG("read, REPLY: pathid = 0x%04X, msgid = 0x%08X, trgcls = " | ||
231 | "0x%08X\n\n", | ||
232 | monmsg->local_eib.ippathid, monmsg->local_eib.ipmsgid, | ||
233 | monmsg->local_eib.iptrgcls); | ||
234 | rc = iucv_reply_prmmsg(monmsg->local_eib.ippathid, | ||
235 | monmsg->local_eib.ipmsgid, | ||
236 | monmsg->local_eib.iptrgcls, | ||
237 | 0, prmmsg); | ||
238 | atomic_dec(&monpriv->msglim_count); | ||
239 | if (likely(!monmsg->msglim_reached)) { | ||
240 | monmsg->pos = 0; | ||
241 | monmsg->mca_offset = 0; | ||
242 | monpriv->read_index = (monpriv->read_index + 1) % | ||
243 | MON_MSGLIM; | ||
244 | atomic_dec(&monpriv->read_ready); | ||
245 | } else | ||
246 | monmsg->replied_msglim = 1; | ||
247 | if (rc) { | ||
248 | P_ERROR("read, IUCV reply failed with rc = %i\n\n", rc); | ||
249 | return -EIO; | ||
250 | } | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static inline struct mon_private * | ||
255 | mon_alloc_mem(void) | ||
256 | { | ||
257 | int i,j; | ||
258 | struct mon_private *monpriv; | ||
259 | |||
260 | monpriv = kmalloc(sizeof(struct mon_private), GFP_KERNEL); | ||
261 | if (!monpriv) { | ||
262 | P_ERROR("no memory for monpriv\n"); | ||
263 | return NULL; | ||
264 | } | ||
265 | memset(monpriv, 0, sizeof(struct mon_private)); | ||
266 | for (i = 0; i < MON_MSGLIM; i++) { | ||
267 | monpriv->msg_array[i] = kmalloc(sizeof(struct mon_msg), | ||
268 | GFP_KERNEL); | ||
269 | if (!monpriv->msg_array[i]) { | ||
270 | P_ERROR("open, no memory for msg_array\n"); | ||
271 | for (j = 0; j < i; j++) | ||
272 | kfree(monpriv->msg_array[j]); | ||
273 | return NULL; | ||
274 | } | ||
275 | memset(monpriv->msg_array[i], 0, sizeof(struct mon_msg)); | ||
276 | } | ||
277 | return monpriv; | ||
278 | } | ||
279 | |||
280 | static inline void | ||
281 | mon_read_debug(struct mon_msg *monmsg, struct mon_private *monpriv) | ||
282 | { | ||
283 | #ifdef MON_DEBUG | ||
284 | u8 msg_type[2], mca_type; | ||
285 | unsigned long records_len; | ||
286 | |||
287 | records_len = mon_rec_end(monmsg) - mon_rec_start(monmsg) + 1; | ||
288 | |||
289 | memcpy(msg_type, &monmsg->local_eib.iptrgcls, 2); | ||
290 | EBCASC(msg_type, 2); | ||
291 | mca_type = mon_mca_type(monmsg, 0); | ||
292 | EBCASC(&mca_type, 1); | ||
293 | |||
294 | P_DEBUG("read, mon_read_index = %i, mon_write_index = %i\n", | ||
295 | monpriv->read_index, monpriv->write_index); | ||
296 | P_DEBUG("read, pathid = 0x%04X, msgid = 0x%08X, trgcls = 0x%08X\n", | ||
297 | monmsg->local_eib.ippathid, monmsg->local_eib.ipmsgid, | ||
298 | monmsg->local_eib.iptrgcls); | ||
299 | P_DEBUG("read, msg_type = '%c%c', mca_type = '%c' / 0x%X / 0x%X\n", | ||
300 | msg_type[0], msg_type[1], mca_type ? mca_type : 'X', | ||
301 | mon_mca_type(monmsg, 1), mon_mca_type(monmsg, 2)); | ||
302 | P_DEBUG("read, MCA: start = 0x%lX, end = 0x%lX\n", | ||
303 | mon_mca_start(monmsg), mon_mca_end(monmsg)); | ||
304 | P_DEBUG("read, REC: start = 0x%X, end = 0x%X, len = %lu\n\n", | ||
305 | mon_rec_start(monmsg), mon_rec_end(monmsg), records_len); | ||
306 | if (mon_mca_size(monmsg) > 12) | ||
307 | P_DEBUG("READ, MORE THAN ONE MCA\n\n"); | ||
308 | #endif | ||
309 | } | ||
310 | |||
311 | static inline void | ||
312 | mon_next_mca(struct mon_msg *monmsg) | ||
313 | { | ||
314 | if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12)) | ||
315 | return; | ||
316 | P_DEBUG("READ, NEXT MCA\n\n"); | ||
317 | monmsg->mca_offset += 12; | ||
318 | monmsg->pos = 0; | ||
319 | } | ||
320 | |||
321 | static inline struct mon_msg * | ||
322 | mon_next_message(struct mon_private *monpriv) | ||
323 | { | ||
324 | struct mon_msg *monmsg; | ||
325 | |||
326 | if (!atomic_read(&monpriv->read_ready)) | ||
327 | return NULL; | ||
328 | monmsg = monpriv->msg_array[monpriv->read_index]; | ||
329 | if (unlikely(monmsg->replied_msglim)) { | ||
330 | monmsg->replied_msglim = 0; | ||
331 | monmsg->msglim_reached = 0; | ||
332 | monmsg->pos = 0; | ||
333 | monmsg->mca_offset = 0; | ||
334 | P_WARNING("read, message limit reached\n"); | ||
335 | monpriv->read_index = (monpriv->read_index + 1) % | ||
336 | MON_MSGLIM; | ||
337 | atomic_dec(&monpriv->read_ready); | ||
338 | return ERR_PTR(-EOVERFLOW); | ||
339 | } | ||
340 | return monmsg; | ||
341 | } | ||
342 | |||
343 | |||
344 | /****************************************************************************** | ||
345 | * IUCV handler * | ||
346 | *****************************************************************************/ | ||
347 | static void | ||
348 | mon_iucv_ConnectionComplete(iucv_ConnectionComplete *eib, void *pgm_data) | ||
349 | { | ||
350 | struct mon_private *monpriv = (struct mon_private *) pgm_data; | ||
351 | |||
352 | P_DEBUG("IUCV connection completed\n"); | ||
353 | P_DEBUG("IUCV ACCEPT (from *MONITOR): Version = 0x%02X, Event = " | ||
354 | "0x%02X, Sample = 0x%02X\n", | ||
355 | eib->ipuser[0], eib->ipuser[1], eib->ipuser[2]); | ||
356 | atomic_set(&monpriv->iucv_connected, 1); | ||
357 | wake_up(&mon_conn_wait_queue); | ||
358 | } | ||
359 | |||
360 | static void | ||
361 | mon_iucv_ConnectionSevered(iucv_ConnectionSevered *eib, void *pgm_data) | ||
362 | { | ||
363 | struct mon_private *monpriv = (struct mon_private *) pgm_data; | ||
364 | |||
365 | P_ERROR("IUCV connection severed with rc = 0x%X\n", | ||
366 | (u8) eib->ipuser[0]); | ||
367 | atomic_set(&monpriv->iucv_severed, 1); | ||
368 | wake_up(&mon_conn_wait_queue); | ||
369 | wake_up_interruptible(&mon_read_wait_queue); | ||
370 | } | ||
371 | |||
372 | static void | ||
373 | mon_iucv_MessagePending(iucv_MessagePending *eib, void *pgm_data) | ||
374 | { | ||
375 | struct mon_private *monpriv = (struct mon_private *) pgm_data; | ||
376 | |||
377 | P_DEBUG("IUCV message pending\n"); | ||
378 | memcpy(&monpriv->msg_array[monpriv->write_index]->local_eib, eib, | ||
379 | sizeof(iucv_MessagePending)); | ||
380 | if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) { | ||
381 | P_WARNING("IUCV message pending, message limit (%i) reached\n", | ||
382 | MON_MSGLIM); | ||
383 | monpriv->msg_array[monpriv->write_index]->msglim_reached = 1; | ||
384 | } | ||
385 | monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM; | ||
386 | atomic_inc(&monpriv->read_ready); | ||
387 | wake_up_interruptible(&mon_read_wait_queue); | ||
388 | } | ||
389 | |||
390 | static iucv_interrupt_ops_t mon_iucvops = { | ||
391 | .ConnectionComplete = mon_iucv_ConnectionComplete, | ||
392 | .ConnectionSevered = mon_iucv_ConnectionSevered, | ||
393 | .MessagePending = mon_iucv_MessagePending, | ||
394 | }; | ||
395 | |||
396 | /****************************************************************************** | ||
397 | * file operations * | ||
398 | *****************************************************************************/ | ||
399 | static int | ||
400 | mon_open(struct inode *inode, struct file *filp) | ||
401 | { | ||
402 | int rc, i; | ||
403 | struct mon_private *monpriv; | ||
404 | |||
405 | /* | ||
406 | * only one user allowed | ||
407 | */ | ||
408 | if (test_and_set_bit(MON_IN_USE, &mon_in_use)) | ||
409 | return -EBUSY; | ||
410 | |||
411 | monpriv = mon_alloc_mem(); | ||
412 | if (!monpriv) | ||
413 | return -ENOMEM; | ||
414 | |||
415 | /* | ||
416 | * Register with IUCV and connect to *MONITOR service | ||
417 | */ | ||
418 | monpriv->iucv_handle = iucv_register_program("my_monreader ", | ||
419 | MON_SERVICE, | ||
420 | NULL, | ||
421 | &mon_iucvops, | ||
422 | monpriv); | ||
423 | if (!monpriv->iucv_handle) { | ||
424 | P_ERROR("failed to register with iucv driver\n"); | ||
425 | rc = -EIO; | ||
426 | goto out_error; | ||
427 | } | ||
428 | P_INFO("open, registered with IUCV\n"); | ||
429 | |||
430 | rc = iucv_connect(&monpriv->pathid, MON_MSGLIM, user_data_connect, | ||
431 | MON_SERVICE, iucv_host, IPRMDATA, NULL, NULL, | ||
432 | monpriv->iucv_handle, NULL); | ||
433 | if (rc) { | ||
434 | P_ERROR("iucv connection to *MONITOR failed with " | ||
435 | "IPUSER SEVER code = %i\n", rc); | ||
436 | rc = -EIO; | ||
437 | goto out_unregister; | ||
438 | } | ||
439 | /* | ||
440 | * Wait for connection confirmation | ||
441 | */ | ||
442 | wait_event(mon_conn_wait_queue, | ||
443 | atomic_read(&monpriv->iucv_connected) || | ||
444 | atomic_read(&monpriv->iucv_severed)); | ||
445 | if (atomic_read(&monpriv->iucv_severed)) { | ||
446 | atomic_set(&monpriv->iucv_severed, 0); | ||
447 | atomic_set(&monpriv->iucv_connected, 0); | ||
448 | rc = -EIO; | ||
449 | goto out_unregister; | ||
450 | } | ||
451 | P_INFO("open, established connection to *MONITOR service\n\n"); | ||
452 | filp->private_data = monpriv; | ||
453 | return nonseekable_open(inode, filp); | ||
454 | |||
455 | out_unregister: | ||
456 | iucv_unregister_program(monpriv->iucv_handle); | ||
457 | out_error: | ||
458 | for (i = 0; i < MON_MSGLIM; i++) | ||
459 | kfree(monpriv->msg_array[i]); | ||
460 | kfree(monpriv); | ||
461 | clear_bit(MON_IN_USE, &mon_in_use); | ||
462 | return rc; | ||
463 | } | ||
464 | |||
465 | static int | ||
466 | mon_close(struct inode *inode, struct file *filp) | ||
467 | { | ||
468 | int rc, i; | ||
469 | struct mon_private *monpriv = filp->private_data; | ||
470 | |||
471 | /* | ||
472 | * Close IUCV connection and unregister | ||
473 | */ | ||
474 | rc = iucv_sever(monpriv->pathid, user_data_sever); | ||
475 | if (rc) | ||
476 | P_ERROR("close, iucv_sever failed with rc = %i\n", rc); | ||
477 | else | ||
478 | P_INFO("close, terminated connection to *MONITOR service\n"); | ||
479 | |||
480 | rc = iucv_unregister_program(monpriv->iucv_handle); | ||
481 | if (rc) | ||
482 | P_ERROR("close, iucv_unregister failed with rc = %i\n", rc); | ||
483 | else | ||
484 | P_INFO("close, unregistered with IUCV\n"); | ||
485 | |||
486 | atomic_set(&monpriv->iucv_severed, 0); | ||
487 | atomic_set(&monpriv->iucv_connected, 0); | ||
488 | atomic_set(&monpriv->read_ready, 0); | ||
489 | atomic_set(&monpriv->msglim_count, 0); | ||
490 | monpriv->write_index = 0; | ||
491 | monpriv->read_index = 0; | ||
492 | |||
493 | for (i = 0; i < MON_MSGLIM; i++) | ||
494 | kfree(monpriv->msg_array[i]); | ||
495 | kfree(monpriv); | ||
496 | clear_bit(MON_IN_USE, &mon_in_use); | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static ssize_t | ||
501 | mon_read(struct file *filp, char __user *data, size_t count, loff_t *ppos) | ||
502 | { | ||
503 | struct mon_private *monpriv = filp->private_data; | ||
504 | struct mon_msg *monmsg; | ||
505 | int ret; | ||
506 | u32 mce_start; | ||
507 | |||
508 | monmsg = mon_next_message(monpriv); | ||
509 | if (IS_ERR(monmsg)) | ||
510 | return PTR_ERR(monmsg); | ||
511 | |||
512 | if (!monmsg) { | ||
513 | if (filp->f_flags & O_NONBLOCK) | ||
514 | return -EAGAIN; | ||
515 | ret = wait_event_interruptible(mon_read_wait_queue, | ||
516 | atomic_read(&monpriv->read_ready) || | ||
517 | atomic_read(&monpriv->iucv_severed)); | ||
518 | if (ret) | ||
519 | return ret; | ||
520 | if (unlikely(atomic_read(&monpriv->iucv_severed))) | ||
521 | return -EIO; | ||
522 | monmsg = monpriv->msg_array[monpriv->read_index]; | ||
523 | } | ||
524 | |||
525 | if (!monmsg->pos) { | ||
526 | monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset; | ||
527 | mon_read_debug(monmsg, monpriv); | ||
528 | } | ||
529 | if (mon_check_mca(monmsg)) | ||
530 | goto reply; | ||
531 | |||
532 | /* read monitor control element (12 bytes) first */ | ||
533 | mce_start = mon_mca_start(monmsg) + monmsg->mca_offset; | ||
534 | if ((monmsg->pos >= mce_start) && (monmsg->pos < mce_start + 12)) { | ||
535 | count = min(count, (size_t) mce_start + 12 - monmsg->pos); | ||
536 | ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos, | ||
537 | count); | ||
538 | if (ret) | ||
539 | return -EFAULT; | ||
540 | monmsg->pos += count; | ||
541 | if (monmsg->pos == mce_start + 12) | ||
542 | monmsg->pos = mon_rec_start(monmsg); | ||
543 | goto out_copy; | ||
544 | } | ||
545 | |||
546 | /* read records */ | ||
547 | if (monmsg->pos <= mon_rec_end(monmsg)) { | ||
548 | count = min(count, (size_t) mon_rec_end(monmsg) - monmsg->pos | ||
549 | + 1); | ||
550 | ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos, | ||
551 | count); | ||
552 | if (ret) | ||
553 | return -EFAULT; | ||
554 | monmsg->pos += count; | ||
555 | if (monmsg->pos > mon_rec_end(monmsg)) | ||
556 | mon_next_mca(monmsg); | ||
557 | goto out_copy; | ||
558 | } | ||
559 | reply: | ||
560 | ret = mon_send_reply(monmsg, monpriv); | ||
561 | return ret; | ||
562 | |||
563 | out_copy: | ||
564 | *ppos += count; | ||
565 | return count; | ||
566 | } | ||
567 | |||
568 | static unsigned int | ||
569 | mon_poll(struct file *filp, struct poll_table_struct *p) | ||
570 | { | ||
571 | struct mon_private *monpriv = filp->private_data; | ||
572 | |||
573 | poll_wait(filp, &mon_read_wait_queue, p); | ||
574 | if (unlikely(atomic_read(&monpriv->iucv_severed))) | ||
575 | return POLLERR; | ||
576 | if (atomic_read(&monpriv->read_ready)) | ||
577 | return POLLIN | POLLRDNORM; | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | static struct file_operations mon_fops = { | ||
582 | .owner = THIS_MODULE, | ||
583 | .open = &mon_open, | ||
584 | .release = &mon_close, | ||
585 | .read = &mon_read, | ||
586 | .poll = &mon_poll, | ||
587 | }; | ||
588 | |||
589 | static struct miscdevice mon_dev = { | ||
590 | .name = "monreader", | ||
591 | .devfs_name = "monreader", | ||
592 | .fops = &mon_fops, | ||
593 | .minor = MISC_DYNAMIC_MINOR, | ||
594 | }; | ||
595 | |||
596 | /****************************************************************************** | ||
597 | * module init/exit * | ||
598 | *****************************************************************************/ | ||
599 | static int __init | ||
600 | mon_init(void) | ||
601 | { | ||
602 | int rc; | ||
603 | |||
604 | if (!MACHINE_IS_VM) { | ||
605 | P_ERROR("not running under z/VM, driver not loaded\n"); | ||
606 | return -ENODEV; | ||
607 | } | ||
608 | |||
609 | rc = segment_type(mon_dcss_name); | ||
610 | if (rc < 0) { | ||
611 | mon_segment_warn(rc, mon_dcss_name); | ||
612 | return rc; | ||
613 | } | ||
614 | if (rc != SEG_TYPE_SC) { | ||
615 | P_ERROR("segment %s has unsupported type, should be SC\n", | ||
616 | mon_dcss_name); | ||
617 | return -EINVAL; | ||
618 | } | ||
619 | |||
620 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, | ||
621 | &mon_dcss_start, &mon_dcss_end); | ||
622 | if (rc < 0) { | ||
623 | mon_segment_warn(rc, mon_dcss_name); | ||
624 | return -EINVAL; | ||
625 | } | ||
626 | dcss_mkname(mon_dcss_name, &user_data_connect[8]); | ||
627 | |||
628 | rc = misc_register(&mon_dev); | ||
629 | if (rc < 0 ) { | ||
630 | P_ERROR("misc_register failed, rc = %i\n", rc); | ||
631 | goto out; | ||
632 | } | ||
633 | P_INFO("Loaded segment %s from %p to %p, size = %lu Byte\n", | ||
634 | mon_dcss_name, (void *) mon_dcss_start, (void *) mon_dcss_end, | ||
635 | mon_dcss_end - mon_dcss_start + 1); | ||
636 | return 0; | ||
637 | |||
638 | out: | ||
639 | segment_unload(mon_dcss_name); | ||
640 | return rc; | ||
641 | } | ||
642 | |||
643 | static void __exit | ||
644 | mon_exit(void) | ||
645 | { | ||
646 | segment_unload(mon_dcss_name); | ||
647 | WARN_ON(misc_deregister(&mon_dev) != 0); | ||
648 | return; | ||
649 | } | ||
650 | |||
651 | |||
652 | module_init(mon_init); | ||
653 | module_exit(mon_exit); | ||
654 | |||
655 | module_param_string(mondcss, mon_dcss_name, 9, 0444); | ||
656 | MODULE_PARM_DESC(mondcss, "Name of DCSS segment to be used for *MONITOR " | ||
657 | "service, max. 8 chars. Default is MONDCSS"); | ||
658 | |||
659 | MODULE_AUTHOR("Gerald Schaefer <geraldsc@de.ibm.com>"); | ||
660 | MODULE_DESCRIPTION("Character device driver for reading z/VM " | ||
661 | "monitor service records."); | ||
662 | MODULE_LICENSE("GPL"); | ||