aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/char/vmlogrdr.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/char/vmlogrdr.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/s390/char/vmlogrdr.c')
-rw-r--r--drivers/s390/char/vmlogrdr.c920
1 files changed, 920 insertions, 0 deletions
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
new file mode 100644
index 000000000000..edf50d2bd10b
--- /dev/null
+++ b/drivers/s390/char/vmlogrdr.c
@@ -0,0 +1,920 @@
1/*
2 * drivers/s390/char/vmlogrdr.c
3 * character device driver for reading z/VM system service records
4 *
5 *
6 * Copyright (C) 2004 IBM Corporation
7 * character device driver for reading z/VM system service records,
8 * Version 1.0
9 * Author(s): Xenia Tkatschow <xenia@us.ibm.com>
10 * Stefan Weinhuber <wein@de.ibm.com>
11 *
12 */
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/errno.h>
16#include <linux/types.h>
17#include <linux/interrupt.h>
18#include <linux/spinlock.h>
19#include <asm/atomic.h>
20#include <asm/uaccess.h>
21#include <asm/cpcmd.h>
22#include <asm/debug.h>
23#include <asm/ebcdic.h>
24#include "../net/iucv.h"
25#include <linux/kmod.h>
26#include <linux/cdev.h>
27#include <linux/device.h>
28#include <linux/string.h>
29
30
31
32MODULE_AUTHOR
33 ("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
34 " Stefan Weinhuber (wein@de.ibm.com)");
35MODULE_DESCRIPTION ("Character device driver for reading z/VM "
36 "system service records.");
37MODULE_LICENSE("GPL");
38
39
40/*
41 * The size of the buffer for iucv data transfer is one page,
42 * but in addition to the data we read from iucv we also
43 * place an integer and some characters into that buffer,
44 * so the maximum size for record data is a little less then
45 * one page.
46 */
47#define NET_BUFFER_SIZE (PAGE_SIZE - sizeof(int) - sizeof(FENCE))
48
49/*
50 * The elements that are concurrently accessed by bottom halves are
51 * connection_established, iucv_path_severed, local_interrupt_buffer
52 * and receive_ready. The first three can be protected by
53 * priv_lock. receive_ready is atomic, so it can be incremented and
54 * decremented without holding a lock.
55 * The variable dev_in_use needs to be protected by the lock, since
56 * it's a flag used by open to make sure that the device is opened only
57 * by one user at the same time.
58 */
59struct vmlogrdr_priv_t {
60 char system_service[8];
61 char internal_name[8];
62 char recording_name[8];
63 u16 pathid;
64 int connection_established;
65 int iucv_path_severed;
66 iucv_MessagePending local_interrupt_buffer;
67 atomic_t receive_ready;
68 iucv_handle_t iucv_handle;
69 int minor_num;
70 char * buffer;
71 char * current_position;
72 int remaining;
73 ulong residual_length;
74 int buffer_free;
75 int dev_in_use; /* 1: already opened, 0: not opened*/
76 spinlock_t priv_lock;
77 struct device *device;
78 struct class_device *class_device;
79 int autorecording;
80 int autopurge;
81};
82
83
84/*
85 * File operation structure for vmlogrdr devices
86 */
87static int vmlogrdr_open(struct inode *, struct file *);
88static int vmlogrdr_release(struct inode *, struct file *);
89static ssize_t vmlogrdr_read (struct file *filp, char *data, size_t count,
90 loff_t * ppos);
91
92static struct file_operations vmlogrdr_fops = {
93 .owner = THIS_MODULE,
94 .open = vmlogrdr_open,
95 .release = vmlogrdr_release,
96 .read = vmlogrdr_read,
97};
98
99
100static u8 iucvMagic[16] = {
101 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
102 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
103};
104
105
106static u8 mask[] = {
107 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
108 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
109 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
110 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
111};
112
113
114static u8 iucv_host[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
115
116
117static void
118vmlogrdr_iucv_ConnectionComplete(iucv_ConnectionComplete *eib, void *pgm_data);
119static void
120vmlogrdr_iucv_ConnectionSevered(iucv_ConnectionSevered *eib, void *pgm_data);
121static void
122vmlogrdr_iucv_MessagePending(iucv_MessagePending *eib, void *pgm_data);
123
124
125static iucv_interrupt_ops_t vmlogrdr_iucvops = {
126 .ConnectionComplete = vmlogrdr_iucv_ConnectionComplete,
127 .ConnectionSevered = vmlogrdr_iucv_ConnectionSevered,
128 .MessagePending = vmlogrdr_iucv_MessagePending,
129};
130
131
132DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
133DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
134
135/*
136 * pointer to system service private structure
137 * minor number 0 --> logrec
138 * minor number 1 --> account
139 * minor number 2 --> symptom
140 */
141
142static struct vmlogrdr_priv_t sys_ser[] = {
143 { .system_service = "*LOGREC ",
144 .internal_name = "logrec",
145 .recording_name = "EREP",
146 .minor_num = 0,
147 .buffer_free = 1,
148 .priv_lock = SPIN_LOCK_UNLOCKED,
149 .autorecording = 1,
150 .autopurge = 1,
151 },
152 { .system_service = "*ACCOUNT",
153 .internal_name = "account",
154 .recording_name = "ACCOUNT",
155 .minor_num = 1,
156 .buffer_free = 1,
157 .priv_lock = SPIN_LOCK_UNLOCKED,
158 .autorecording = 1,
159 .autopurge = 1,
160 },
161 { .system_service = "*SYMPTOM",
162 .internal_name = "symptom",
163 .recording_name = "SYMPTOM",
164 .minor_num = 2,
165 .buffer_free = 1,
166 .priv_lock = SPIN_LOCK_UNLOCKED,
167 .autorecording = 1,
168 .autopurge = 1,
169 }
170};
171
172#define MAXMINOR (sizeof(sys_ser)/sizeof(struct vmlogrdr_priv_t))
173
174static char FENCE[] = {"EOR"};
175static int vmlogrdr_major = 0;
176static struct cdev *vmlogrdr_cdev = NULL;
177static int recording_class_AB;
178
179
180static void
181vmlogrdr_iucv_ConnectionComplete (iucv_ConnectionComplete * eib,
182 void * pgm_data)
183{
184 struct vmlogrdr_priv_t * logptr = pgm_data;
185 spin_lock(&logptr->priv_lock);
186 logptr->connection_established = 1;
187 spin_unlock(&logptr->priv_lock);
188 wake_up(&conn_wait_queue);
189 return;
190}
191
192
193static void
194vmlogrdr_iucv_ConnectionSevered (iucv_ConnectionSevered * eib, void * pgm_data)
195{
196 u8 reason = (u8) eib->ipuser[8];
197 struct vmlogrdr_priv_t * logptr = pgm_data;
198
199 printk (KERN_ERR "vmlogrdr: connection severed with"
200 " reason %i\n", reason);
201
202 spin_lock(&logptr->priv_lock);
203 logptr->connection_established = 0;
204 logptr->iucv_path_severed = 1;
205 spin_unlock(&logptr->priv_lock);
206
207 wake_up(&conn_wait_queue);
208 /* just in case we're sleeping waiting for a record */
209 wake_up_interruptible(&read_wait_queue);
210}
211
212
213static void
214vmlogrdr_iucv_MessagePending (iucv_MessagePending * eib, void * pgm_data)
215{
216 struct vmlogrdr_priv_t * logptr = pgm_data;
217
218 /*
219 * This function is the bottom half so it should be quick.
220 * Copy the external interrupt data into our local eib and increment
221 * the usage count
222 */
223 spin_lock(&logptr->priv_lock);
224 memcpy(&(logptr->local_interrupt_buffer), eib, sizeof(*eib));
225 atomic_inc(&logptr->receive_ready);
226 spin_unlock(&logptr->priv_lock);
227 wake_up_interruptible(&read_wait_queue);
228}
229
230
231static int
232vmlogrdr_get_recording_class_AB(void) {
233 char cp_command[]="QUERY COMMAND RECORDING ";
234 char cp_response[80];
235 char *tail;
236 int len,i;
237
238 printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command);
239 cpcmd(cp_command, cp_response, sizeof(cp_response));
240 printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response);
241 len = strnlen(cp_response,sizeof(cp_response));
242 // now the parsing
243 tail=strnchr(cp_response,len,'=');
244 if (!tail)
245 return 0;
246 tail++;
247 if (!strncmp("ANY",tail,3))
248 return 1;
249 if (!strncmp("NONE",tail,4))
250 return 0;
251 /*
252 * expect comma separated list of classes here, if one of them
253 * is A or B return 1 otherwise 0
254 */
255 for (i=tail-cp_response; i<len; i++)
256 if ( cp_response[i]=='A' || cp_response[i]=='B' )
257 return 1;
258 return 0;
259}
260
261
262static int
263vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, int action, int purge) {
264
265 char cp_command[80];
266 char cp_response[160];
267 char *onoff, *qid_string;
268
269 memset(cp_command, 0x00, sizeof(cp_command));
270 memset(cp_response, 0x00, sizeof(cp_response));
271
272 onoff = ((action == 1) ? "ON" : "OFF");
273 qid_string = ((recording_class_AB == 1) ? " QID * " : "");
274
275 /*
276 * The recording commands needs to be called with option QID
277 * for guests that have previlege classes A or B.
278 * Purging has to be done as separate step, because recording
279 * can't be switched on as long as records are on the queue.
280 * Doing both at the same time doesn't work.
281 */
282
283 if (purge) {
284 snprintf(cp_command, sizeof(cp_command),
285 "RECORDING %s PURGE %s",
286 logptr->recording_name,
287 qid_string);
288
289 printk (KERN_DEBUG "vmlogrdr: recording command: %s\n",
290 cp_command);
291 cpcmd(cp_command, cp_response, sizeof(cp_response));
292 printk (KERN_DEBUG "vmlogrdr: recording response: %s",
293 cp_response);
294 }
295
296 memset(cp_command, 0x00, sizeof(cp_command));
297 memset(cp_response, 0x00, sizeof(cp_response));
298 snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
299 logptr->recording_name,
300 onoff,
301 qid_string);
302
303 printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
304 cpcmd(cp_command, cp_response, sizeof(cp_response));
305 printk (KERN_DEBUG "vmlogrdr: recording response: %s",
306 cp_response);
307 /* The recording command will usually answer with 'Command complete'
308 * on success, but when the specific service was never connected
309 * before then there might be an additional informational message
310 * 'HCPCRC8072I Recording entry not found' before the
311 * 'Command complete'. So I use strstr rather then the strncmp.
312 */
313 if (strstr(cp_response,"Command complete"))
314 return 0;
315 else
316 return -EIO;
317
318}
319
320
321static int
322vmlogrdr_open (struct inode *inode, struct file *filp)
323{
324 int dev_num = 0;
325 struct vmlogrdr_priv_t * logptr = NULL;
326 int connect_rc = 0;
327 int ret;
328
329 dev_num = iminor(inode);
330 if (dev_num > MAXMINOR)
331 return -ENODEV;
332
333 logptr = &sys_ser[dev_num];
334 if (logptr == NULL)
335 return -ENODEV;
336
337 /*
338 * only allow for blocking reads to be open
339 */
340 if (filp->f_flags & O_NONBLOCK)
341 return -ENOSYS;
342
343 /* Besure this device hasn't already been opened */
344 spin_lock_bh(&logptr->priv_lock);
345 if (logptr->dev_in_use) {
346 spin_unlock_bh(&logptr->priv_lock);
347 return -EBUSY;
348 } else {
349 logptr->dev_in_use = 1;
350 spin_unlock_bh(&logptr->priv_lock);
351 }
352
353 atomic_set(&logptr->receive_ready, 0);
354 logptr->buffer_free = 1;
355
356 /* set the file options */
357 filp->private_data = logptr;
358 filp->f_op = &vmlogrdr_fops;
359
360 /* start recording for this service*/
361 ret=0;
362 if (logptr->autorecording)
363 ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
364 if (ret)
365 printk (KERN_WARNING "vmlogrdr: failed to start "
366 "recording automatically\n");
367
368 /* Register with iucv driver */
369 logptr->iucv_handle = iucv_register_program(iucvMagic,
370 logptr->system_service, mask, &vmlogrdr_iucvops,
371 logptr);
372
373 if (logptr->iucv_handle == NULL) {
374 printk (KERN_ERR "vmlogrdr: failed to register with"
375 "iucv driver\n");
376 goto not_registered;
377 }
378
379 /* create connection to the system service */
380 spin_lock_bh(&logptr->priv_lock);
381 logptr->connection_established = 0;
382 logptr->iucv_path_severed = 0;
383 spin_unlock_bh(&logptr->priv_lock);
384
385 connect_rc = iucv_connect (&(logptr->pathid), 10, iucvMagic,
386 logptr->system_service, iucv_host, 0,
387 NULL, NULL,
388 logptr->iucv_handle, NULL);
389 if (connect_rc) {
390 printk (KERN_ERR "vmlogrdr: iucv connection to %s "
391 "failed with rc %i \n", logptr->system_service,
392 connect_rc);
393 goto not_connected;
394 }
395
396 /* We've issued the connect and now we must wait for a
397 * ConnectionComplete or ConnectinSevered Interrupt
398 * before we can continue to process.
399 */
400 wait_event(conn_wait_queue, (logptr->connection_established)
401 || (logptr->iucv_path_severed));
402 if (logptr->iucv_path_severed) {
403 goto not_connected;
404 }
405
406 return nonseekable_open(inode, filp);
407
408not_connected:
409 iucv_unregister_program(logptr->iucv_handle);
410 logptr->iucv_handle = NULL;
411not_registered:
412 if (logptr->autorecording)
413 vmlogrdr_recording(logptr,0,logptr->autopurge);
414 logptr->dev_in_use = 0;
415 return -EIO;
416
417
418}
419
420
421static int
422vmlogrdr_release (struct inode *inode, struct file *filp)
423{
424 int ret;
425
426 struct vmlogrdr_priv_t * logptr = filp->private_data;
427
428 iucv_unregister_program(logptr->iucv_handle);
429 logptr->iucv_handle = NULL;
430
431 if (logptr->autorecording) {
432 ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
433 if (ret)
434 printk (KERN_WARNING "vmlogrdr: failed to stop "
435 "recording automatically\n");
436 }
437 logptr->dev_in_use = 0;
438
439 return 0;
440}
441
442
443static int
444vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv) {
445 int rc, *temp;
446 /* we need to keep track of two data sizes here:
447 * The number of bytes we need to receive from iucv and
448 * the total number of bytes we actually write into the buffer.
449 */
450 int user_data_count, iucv_data_count;
451 char * buffer;
452
453 if (atomic_read(&priv->receive_ready)) {
454 spin_lock_bh(&priv->priv_lock);
455 if (priv->residual_length){
456 /* receive second half of a record */
457 iucv_data_count = priv->residual_length;
458 user_data_count = 0;
459 buffer = priv->buffer;
460 } else {
461 /* receive a new record:
462 * We need to return the total length of the record
463 * + size of FENCE in the first 4 bytes of the buffer.
464 */
465 iucv_data_count =
466 priv->local_interrupt_buffer.ln1msg2.ipbfln1f;
467 user_data_count = sizeof(int);
468 temp = (int*)priv->buffer;
469 *temp= iucv_data_count + sizeof(FENCE);
470 buffer = priv->buffer + sizeof(int);
471 }
472 /*
473 * If the record is bigger then our buffer, we receive only
474 * a part of it. We can get the rest later.
475 */
476 if (iucv_data_count > NET_BUFFER_SIZE)
477 iucv_data_count = NET_BUFFER_SIZE;
478 rc = iucv_receive(priv->pathid,
479 priv->local_interrupt_buffer.ipmsgid,
480 priv->local_interrupt_buffer.iptrgcls,
481 buffer,
482 iucv_data_count,
483 NULL,
484 NULL,
485 &priv->residual_length);
486 spin_unlock_bh(&priv->priv_lock);
487 /* An rc of 5 indicates that the record was bigger then
488 * the buffer, which is OK for us. A 9 indicates that the
489 * record was purged befor we could receive it.
490 */
491 if (rc == 5)
492 rc = 0;
493 if (rc == 9)
494 atomic_set(&priv->receive_ready, 0);
495 } else {
496 rc = 1;
497 }
498 if (!rc) {
499 priv->buffer_free = 0;
500 user_data_count += iucv_data_count;
501 priv->current_position = priv->buffer;
502 if (priv->residual_length == 0){
503 /* the whole record has been captured,
504 * now add the fence */
505 atomic_dec(&priv->receive_ready);
506 buffer = priv->buffer + user_data_count;
507 memcpy(buffer, FENCE, sizeof(FENCE));
508 user_data_count += sizeof(FENCE);
509 }
510 priv->remaining = user_data_count;
511 }
512
513 return rc;
514}
515
516
517static ssize_t
518vmlogrdr_read (struct file *filp, char *data, size_t count, loff_t * ppos)
519{
520 int rc;
521 struct vmlogrdr_priv_t * priv = filp->private_data;
522
523 while (priv->buffer_free) {
524 rc = vmlogrdr_receive_data(priv);
525 if (rc) {
526 rc = wait_event_interruptible(read_wait_queue,
527 atomic_read(&priv->receive_ready));
528 if (rc)
529 return rc;
530 }
531 }
532 /* copy only up to end of record */
533 if (count > priv->remaining)
534 count = priv->remaining;
535
536 if (copy_to_user(data, priv->current_position, count))
537 return -EFAULT;
538
539 *ppos += count;
540 priv->current_position += count;
541 priv->remaining -= count;
542
543 /* if all data has been transferred, set buffer free */
544 if (priv->remaining == 0)
545 priv->buffer_free = 1;
546
547 return count;
548}
549
550static ssize_t
551vmlogrdr_autopurge_store(struct device * dev, const char * buf, size_t count) {
552 struct vmlogrdr_priv_t *priv = dev->driver_data;
553 ssize_t ret = count;
554
555 switch (buf[0]) {
556 case '0':
557 priv->autopurge=0;
558 break;
559 case '1':
560 priv->autopurge=1;
561 break;
562 default:
563 ret = -EINVAL;
564 }
565 return ret;
566}
567
568
569static ssize_t
570vmlogrdr_autopurge_show(struct device *dev, char *buf) {
571 struct vmlogrdr_priv_t *priv = dev->driver_data;
572 return sprintf(buf, "%u\n", priv->autopurge);
573}
574
575
576static DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
577 vmlogrdr_autopurge_store);
578
579
580static ssize_t
581vmlogrdr_purge_store(struct device * dev, const char * buf, size_t count) {
582
583 char cp_command[80];
584 char cp_response[80];
585 struct vmlogrdr_priv_t *priv = dev->driver_data;
586
587 if (buf[0] != '1')
588 return -EINVAL;
589
590 memset(cp_command, 0x00, sizeof(cp_command));
591 memset(cp_response, 0x00, sizeof(cp_response));
592
593 /*
594 * The recording command needs to be called with option QID
595 * for guests that have previlege classes A or B.
596 * Other guests will not recognize the command and we have to
597 * issue the same command without the QID parameter.
598 */
599
600 if (recording_class_AB)
601 snprintf(cp_command, sizeof(cp_command),
602 "RECORDING %s PURGE QID * ",
603 priv->recording_name);
604 else
605 snprintf(cp_command, sizeof(cp_command),
606 "RECORDING %s PURGE ",
607 priv->recording_name);
608
609 printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
610 cpcmd(cp_command, cp_response, sizeof(cp_response));
611 printk (KERN_DEBUG "vmlogrdr: recording response: %s",
612 cp_response);
613
614 return count;
615}
616
617
618static DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
619
620
621static ssize_t
622vmlogrdr_autorecording_store(struct device *dev, const char *buf,
623 size_t count) {
624 struct vmlogrdr_priv_t *priv = dev->driver_data;
625 ssize_t ret = count;
626
627 switch (buf[0]) {
628 case '0':
629 priv->autorecording=0;
630 break;
631 case '1':
632 priv->autorecording=1;
633 break;
634 default:
635 ret = -EINVAL;
636 }
637 return ret;
638}
639
640
641static ssize_t
642vmlogrdr_autorecording_show(struct device *dev, char *buf) {
643 struct vmlogrdr_priv_t *priv = dev->driver_data;
644 return sprintf(buf, "%u\n", priv->autorecording);
645}
646
647
648static DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
649 vmlogrdr_autorecording_store);
650
651
652static ssize_t
653vmlogrdr_recording_store(struct device * dev, const char * buf, size_t count) {
654
655 struct vmlogrdr_priv_t *priv = dev->driver_data;
656 ssize_t ret;
657
658 switch (buf[0]) {
659 case '0':
660 ret = vmlogrdr_recording(priv,0,0);
661 break;
662 case '1':
663 ret = vmlogrdr_recording(priv,1,0);
664 break;
665 default:
666 ret = -EINVAL;
667 }
668 if (ret)
669 return ret;
670 else
671 return count;
672
673}
674
675
676static DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
677
678
679static ssize_t
680vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) {
681
682 char cp_command[] = "QUERY RECORDING ";
683 int len;
684
685 cpcmd(cp_command, buf, 4096);
686 len = strlen(buf);
687 return len;
688}
689
690
691static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
692 NULL);
693
694static struct attribute *vmlogrdr_attrs[] = {
695 &dev_attr_autopurge.attr,
696 &dev_attr_purge.attr,
697 &dev_attr_autorecording.attr,
698 &dev_attr_recording.attr,
699 NULL,
700};
701
702static struct attribute_group vmlogrdr_attr_group = {
703 .attrs = vmlogrdr_attrs,
704};
705
706static struct class_simple *vmlogrdr_class;
707static struct device_driver vmlogrdr_driver = {
708 .name = "vmlogrdr",
709 .bus = &iucv_bus,
710};
711
712
713static int
714vmlogrdr_register_driver(void) {
715 int ret;
716
717 ret = driver_register(&vmlogrdr_driver);
718 if (ret) {
719 printk(KERN_ERR "vmlogrdr: failed to register driver.\n");
720 return ret;
721 }
722
723 ret = driver_create_file(&vmlogrdr_driver,
724 &driver_attr_recording_status);
725 if (ret) {
726 printk(KERN_ERR "vmlogrdr: failed to add driver attribute.\n");
727 goto unregdriver;
728 }
729
730 vmlogrdr_class = class_simple_create(THIS_MODULE, "vmlogrdr");
731 if (IS_ERR(vmlogrdr_class)) {
732 printk(KERN_ERR "vmlogrdr: failed to create class.\n");
733 ret=PTR_ERR(vmlogrdr_class);
734 vmlogrdr_class=NULL;
735 goto unregattr;
736 }
737 return 0;
738
739unregattr:
740 driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
741unregdriver:
742 driver_unregister(&vmlogrdr_driver);
743 return ret;
744}
745
746
747static void
748vmlogrdr_unregister_driver(void) {
749 class_simple_destroy(vmlogrdr_class);
750 vmlogrdr_class = NULL;
751 driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
752 driver_unregister(&vmlogrdr_driver);
753 return;
754}
755
756
757static int
758vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) {
759 struct device *dev;
760 int ret;
761
762 dev = kmalloc(sizeof(struct device), GFP_KERNEL);
763 if (dev) {
764 memset(dev, 0, sizeof(struct device));
765 snprintf(dev->bus_id, BUS_ID_SIZE, "%s",
766 priv->internal_name);
767 dev->bus = &iucv_bus;
768 dev->parent = iucv_root;
769 dev->driver = &vmlogrdr_driver;
770 /*
771 * The release function could be called after the
772 * module has been unloaded. It's _only_ task is to
773 * free the struct. Therefore, we specify kfree()
774 * directly here. (Probably a little bit obfuscating
775 * but legitime ...).
776 */
777 dev->release = (void (*)(struct device *))kfree;
778 } else
779 return -ENOMEM;
780 ret = device_register(dev);
781 if (ret)
782 return ret;
783
784 ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group);
785 if (ret) {
786 device_unregister(dev);
787 return ret;
788 }
789 priv->class_device = class_simple_device_add(
790 vmlogrdr_class,
791 MKDEV(vmlogrdr_major, priv->minor_num),
792 dev,
793 "%s", dev->bus_id );
794 if (IS_ERR(priv->class_device)) {
795 ret = PTR_ERR(priv->class_device);
796 priv->class_device=NULL;
797 sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group);
798 device_unregister(dev);
799 return ret;
800 }
801 dev->driver_data = priv;
802 priv->device = dev;
803 return 0;
804}
805
806
807static int
808vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv ) {
809 class_simple_device_remove(MKDEV(vmlogrdr_major, priv->minor_num));
810 if (priv->device != NULL) {
811 sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group);
812 device_unregister(priv->device);
813 priv->device=NULL;
814 }
815 return 0;
816}
817
818
819static int
820vmlogrdr_register_cdev(dev_t dev) {
821 int rc = 0;
822 vmlogrdr_cdev = cdev_alloc();
823 if (!vmlogrdr_cdev) {
824 return -ENOMEM;
825 }
826 vmlogrdr_cdev->owner = THIS_MODULE;
827 vmlogrdr_cdev->ops = &vmlogrdr_fops;
828 vmlogrdr_cdev->dev = dev;
829 rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
830 if (!rc)
831 return 0;
832
833 // cleanup: cdev is not fully registered, no cdev_del here!
834 kobject_put(&vmlogrdr_cdev->kobj);
835 vmlogrdr_cdev=NULL;
836 return rc;
837}
838
839
840static void
841vmlogrdr_cleanup(void) {
842 int i;
843 if (vmlogrdr_cdev) {
844 cdev_del(vmlogrdr_cdev);
845 vmlogrdr_cdev=NULL;
846 }
847 for (i=0; i < MAXMINOR; ++i ) {
848 vmlogrdr_unregister_device(&sys_ser[i]);
849 free_page((unsigned long)sys_ser[i].buffer);
850 }
851 vmlogrdr_unregister_driver();
852 if (vmlogrdr_major) {
853 unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
854 vmlogrdr_major=0;
855 }
856}
857
858
859static int
860vmlogrdr_init(void)
861{
862 int rc;
863 int i;
864 dev_t dev;
865
866 if (! MACHINE_IS_VM) {
867 printk (KERN_ERR "vmlogrdr: not running under VM, "
868 "driver not loaded.\n");
869 return -ENODEV;
870 }
871
872 recording_class_AB = vmlogrdr_get_recording_class_AB();
873
874 rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
875 if (rc)
876 return rc;
877 vmlogrdr_major = MAJOR(dev);
878
879 rc=vmlogrdr_register_driver();
880 if (rc)
881 goto cleanup;
882
883 for (i=0; i < MAXMINOR; ++i ) {
884 sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
885 if (!sys_ser[i].buffer) {
886 rc = ENOMEM;
887 break;
888 }
889 sys_ser[i].current_position = sys_ser[i].buffer;
890 rc=vmlogrdr_register_device(&sys_ser[i]);
891 if (rc)
892 break;
893 }
894 if (rc)
895 goto cleanup;
896
897 rc = vmlogrdr_register_cdev(dev);
898 if (rc)
899 goto cleanup;
900 printk (KERN_INFO "vmlogrdr: driver loaded\n");
901 return 0;
902
903cleanup:
904 vmlogrdr_cleanup();
905 printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
906 return rc;
907}
908
909
910static void
911vmlogrdr_exit(void)
912{
913 vmlogrdr_cleanup();
914 printk (KERN_INFO "vmlogrdr: driver unloaded\n");
915 return;
916}
917
918
919module_init(vmlogrdr_init);
920module_exit(vmlogrdr_exit);