blob: 4f894dc2373bbc68230f5c2cb9fe31c1f1b913a2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 *);
Heiko Carstensd2c993d2006-07-12 16:41:55 +020089static ssize_t vmlogrdr_read (struct file *filp, char __user *data,
90 size_t count, loff_t * ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
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
Heiko Carstens2b67fc42007-02-05 21:16:47 +0100131static DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
132static DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
134/*
135 * pointer to system service private structure
136 * minor number 0 --> logrec
137 * minor number 1 --> account
138 * minor number 2 --> symptom
139 */
140
141static struct vmlogrdr_priv_t sys_ser[] = {
142 { .system_service = "*LOGREC ",
143 .internal_name = "logrec",
144 .recording_name = "EREP",
145 .minor_num = 0,
146 .buffer_free = 1,
147 .priv_lock = SPIN_LOCK_UNLOCKED,
148 .autorecording = 1,
149 .autopurge = 1,
150 },
151 { .system_service = "*ACCOUNT",
152 .internal_name = "account",
153 .recording_name = "ACCOUNT",
154 .minor_num = 1,
155 .buffer_free = 1,
156 .priv_lock = SPIN_LOCK_UNLOCKED,
157 .autorecording = 1,
158 .autopurge = 1,
159 },
160 { .system_service = "*SYMPTOM",
161 .internal_name = "symptom",
162 .recording_name = "SYMPTOM",
163 .minor_num = 2,
164 .buffer_free = 1,
165 .priv_lock = SPIN_LOCK_UNLOCKED,
166 .autorecording = 1,
167 .autopurge = 1,
168 }
169};
170
171#define MAXMINOR (sizeof(sys_ser)/sizeof(struct vmlogrdr_priv_t))
172
173static char FENCE[] = {"EOR"};
174static int vmlogrdr_major = 0;
175static struct cdev *vmlogrdr_cdev = NULL;
176static int recording_class_AB;
177
178
179static void
180vmlogrdr_iucv_ConnectionComplete (iucv_ConnectionComplete * eib,
181 void * pgm_data)
182{
183 struct vmlogrdr_priv_t * logptr = pgm_data;
184 spin_lock(&logptr->priv_lock);
185 logptr->connection_established = 1;
186 spin_unlock(&logptr->priv_lock);
187 wake_up(&conn_wait_queue);
188 return;
189}
190
191
192static void
193vmlogrdr_iucv_ConnectionSevered (iucv_ConnectionSevered * eib, void * pgm_data)
194{
195 u8 reason = (u8) eib->ipuser[8];
196 struct vmlogrdr_priv_t * logptr = pgm_data;
197
198 printk (KERN_ERR "vmlogrdr: connection severed with"
199 " reason %i\n", reason);
200
201 spin_lock(&logptr->priv_lock);
202 logptr->connection_established = 0;
203 logptr->iucv_path_severed = 1;
204 spin_unlock(&logptr->priv_lock);
205
206 wake_up(&conn_wait_queue);
207 /* just in case we're sleeping waiting for a record */
208 wake_up_interruptible(&read_wait_queue);
209}
210
211
212static void
213vmlogrdr_iucv_MessagePending (iucv_MessagePending * eib, void * pgm_data)
214{
215 struct vmlogrdr_priv_t * logptr = pgm_data;
216
217 /*
218 * This function is the bottom half so it should be quick.
219 * Copy the external interrupt data into our local eib and increment
220 * the usage count
221 */
222 spin_lock(&logptr->priv_lock);
223 memcpy(&(logptr->local_interrupt_buffer), eib, sizeof(*eib));
224 atomic_inc(&logptr->receive_ready);
225 spin_unlock(&logptr->priv_lock);
226 wake_up_interruptible(&read_wait_queue);
227}
228
229
230static int
231vmlogrdr_get_recording_class_AB(void) {
232 char cp_command[]="QUERY COMMAND RECORDING ";
233 char cp_response[80];
234 char *tail;
235 int len,i;
236
237 printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700238 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response);
240 len = strnlen(cp_response,sizeof(cp_response));
241 // now the parsing
242 tail=strnchr(cp_response,len,'=');
243 if (!tail)
244 return 0;
245 tail++;
246 if (!strncmp("ANY",tail,3))
247 return 1;
248 if (!strncmp("NONE",tail,4))
249 return 0;
250 /*
251 * expect comma separated list of classes here, if one of them
252 * is A or B return 1 otherwise 0
253 */
254 for (i=tail-cp_response; i<len; i++)
255 if ( cp_response[i]=='A' || cp_response[i]=='B' )
256 return 1;
257 return 0;
258}
259
260
261static int
262vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, int action, int purge) {
263
264 char cp_command[80];
265 char cp_response[160];
266 char *onoff, *qid_string;
267
268 memset(cp_command, 0x00, sizeof(cp_command));
269 memset(cp_response, 0x00, sizeof(cp_response));
270
271 onoff = ((action == 1) ? "ON" : "OFF");
272 qid_string = ((recording_class_AB == 1) ? " QID * " : "");
273
274 /*
275 * The recording commands needs to be called with option QID
276 * for guests that have previlege classes A or B.
277 * Purging has to be done as separate step, because recording
278 * can't be switched on as long as records are on the queue.
279 * Doing both at the same time doesn't work.
280 */
281
282 if (purge) {
283 snprintf(cp_command, sizeof(cp_command),
284 "RECORDING %s PURGE %s",
285 logptr->recording_name,
286 qid_string);
287
288 printk (KERN_DEBUG "vmlogrdr: recording command: %s\n",
289 cp_command);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700290 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 printk (KERN_DEBUG "vmlogrdr: recording response: %s",
292 cp_response);
293 }
294
295 memset(cp_command, 0x00, sizeof(cp_command));
296 memset(cp_response, 0x00, sizeof(cp_response));
297 snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
298 logptr->recording_name,
299 onoff,
300 qid_string);
301
302 printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700303 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 printk (KERN_DEBUG "vmlogrdr: recording response: %s",
305 cp_response);
306 /* The recording command will usually answer with 'Command complete'
307 * on success, but when the specific service was never connected
308 * before then there might be an additional informational message
309 * 'HCPCRC8072I Recording entry not found' before the
310 * 'Command complete'. So I use strstr rather then the strncmp.
311 */
312 if (strstr(cp_response,"Command complete"))
313 return 0;
314 else
315 return -EIO;
316
317}
318
319
320static int
321vmlogrdr_open (struct inode *inode, struct file *filp)
322{
323 int dev_num = 0;
324 struct vmlogrdr_priv_t * logptr = NULL;
325 int connect_rc = 0;
326 int ret;
327
328 dev_num = iminor(inode);
329 if (dev_num > MAXMINOR)
330 return -ENODEV;
331
332 logptr = &sys_ser[dev_num];
333 if (logptr == NULL)
334 return -ENODEV;
335
336 /*
337 * only allow for blocking reads to be open
338 */
339 if (filp->f_flags & O_NONBLOCK)
340 return -ENOSYS;
341
342 /* Besure this device hasn't already been opened */
343 spin_lock_bh(&logptr->priv_lock);
344 if (logptr->dev_in_use) {
345 spin_unlock_bh(&logptr->priv_lock);
346 return -EBUSY;
347 } else {
348 logptr->dev_in_use = 1;
349 spin_unlock_bh(&logptr->priv_lock);
350 }
351
352 atomic_set(&logptr->receive_ready, 0);
353 logptr->buffer_free = 1;
354
355 /* set the file options */
356 filp->private_data = logptr;
357 filp->f_op = &vmlogrdr_fops;
358
359 /* start recording for this service*/
360 ret=0;
361 if (logptr->autorecording)
362 ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
363 if (ret)
364 printk (KERN_WARNING "vmlogrdr: failed to start "
365 "recording automatically\n");
366
367 /* Register with iucv driver */
368 logptr->iucv_handle = iucv_register_program(iucvMagic,
369 logptr->system_service, mask, &vmlogrdr_iucvops,
370 logptr);
371
372 if (logptr->iucv_handle == NULL) {
373 printk (KERN_ERR "vmlogrdr: failed to register with"
374 "iucv driver\n");
375 goto not_registered;
376 }
377
378 /* create connection to the system service */
379 spin_lock_bh(&logptr->priv_lock);
380 logptr->connection_established = 0;
381 logptr->iucv_path_severed = 0;
382 spin_unlock_bh(&logptr->priv_lock);
383
384 connect_rc = iucv_connect (&(logptr->pathid), 10, iucvMagic,
385 logptr->system_service, iucv_host, 0,
386 NULL, NULL,
387 logptr->iucv_handle, NULL);
388 if (connect_rc) {
389 printk (KERN_ERR "vmlogrdr: iucv connection to %s "
390 "failed with rc %i \n", logptr->system_service,
391 connect_rc);
392 goto not_connected;
393 }
394
395 /* We've issued the connect and now we must wait for a
396 * ConnectionComplete or ConnectinSevered Interrupt
397 * before we can continue to process.
398 */
399 wait_event(conn_wait_queue, (logptr->connection_established)
400 || (logptr->iucv_path_severed));
401 if (logptr->iucv_path_severed) {
402 goto not_connected;
403 }
404
405 return nonseekable_open(inode, filp);
406
407not_connected:
408 iucv_unregister_program(logptr->iucv_handle);
409 logptr->iucv_handle = NULL;
410not_registered:
411 if (logptr->autorecording)
412 vmlogrdr_recording(logptr,0,logptr->autopurge);
413 logptr->dev_in_use = 0;
414 return -EIO;
415
416
417}
418
419
420static int
421vmlogrdr_release (struct inode *inode, struct file *filp)
422{
423 int ret;
424
425 struct vmlogrdr_priv_t * logptr = filp->private_data;
426
427 iucv_unregister_program(logptr->iucv_handle);
428 logptr->iucv_handle = NULL;
429
430 if (logptr->autorecording) {
431 ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
432 if (ret)
433 printk (KERN_WARNING "vmlogrdr: failed to stop "
434 "recording automatically\n");
435 }
436 logptr->dev_in_use = 0;
437
438 return 0;
439}
440
441
442static int
443vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv) {
444 int rc, *temp;
445 /* we need to keep track of two data sizes here:
446 * The number of bytes we need to receive from iucv and
447 * the total number of bytes we actually write into the buffer.
448 */
449 int user_data_count, iucv_data_count;
450 char * buffer;
451
452 if (atomic_read(&priv->receive_ready)) {
453 spin_lock_bh(&priv->priv_lock);
454 if (priv->residual_length){
455 /* receive second half of a record */
456 iucv_data_count = priv->residual_length;
457 user_data_count = 0;
458 buffer = priv->buffer;
459 } else {
460 /* receive a new record:
461 * We need to return the total length of the record
462 * + size of FENCE in the first 4 bytes of the buffer.
463 */
464 iucv_data_count =
465 priv->local_interrupt_buffer.ln1msg2.ipbfln1f;
466 user_data_count = sizeof(int);
467 temp = (int*)priv->buffer;
468 *temp= iucv_data_count + sizeof(FENCE);
469 buffer = priv->buffer + sizeof(int);
470 }
471 /*
472 * If the record is bigger then our buffer, we receive only
473 * a part of it. We can get the rest later.
474 */
475 if (iucv_data_count > NET_BUFFER_SIZE)
476 iucv_data_count = NET_BUFFER_SIZE;
477 rc = iucv_receive(priv->pathid,
478 priv->local_interrupt_buffer.ipmsgid,
479 priv->local_interrupt_buffer.iptrgcls,
480 buffer,
481 iucv_data_count,
482 NULL,
483 NULL,
484 &priv->residual_length);
485 spin_unlock_bh(&priv->priv_lock);
486 /* An rc of 5 indicates that the record was bigger then
487 * the buffer, which is OK for us. A 9 indicates that the
488 * record was purged befor we could receive it.
489 */
490 if (rc == 5)
491 rc = 0;
492 if (rc == 9)
493 atomic_set(&priv->receive_ready, 0);
494 } else {
495 rc = 1;
496 }
497 if (!rc) {
498 priv->buffer_free = 0;
499 user_data_count += iucv_data_count;
500 priv->current_position = priv->buffer;
501 if (priv->residual_length == 0){
502 /* the whole record has been captured,
503 * now add the fence */
504 atomic_dec(&priv->receive_ready);
505 buffer = priv->buffer + user_data_count;
506 memcpy(buffer, FENCE, sizeof(FENCE));
507 user_data_count += sizeof(FENCE);
508 }
509 priv->remaining = user_data_count;
510 }
511
512 return rc;
513}
514
515
516static ssize_t
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200517vmlogrdr_read(struct file *filp, char __user *data, size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518{
519 int rc;
520 struct vmlogrdr_priv_t * priv = filp->private_data;
521
522 while (priv->buffer_free) {
523 rc = vmlogrdr_receive_data(priv);
524 if (rc) {
525 rc = wait_event_interruptible(read_wait_queue,
526 atomic_read(&priv->receive_ready));
527 if (rc)
528 return rc;
529 }
530 }
531 /* copy only up to end of record */
532 if (count > priv->remaining)
533 count = priv->remaining;
534
535 if (copy_to_user(data, priv->current_position, count))
536 return -EFAULT;
537
538 *ppos += count;
539 priv->current_position += count;
540 priv->remaining -= count;
541
542 /* if all data has been transferred, set buffer free */
543 if (priv->remaining == 0)
544 priv->buffer_free = 1;
545
546 return count;
547}
548
549static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400550vmlogrdr_autopurge_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 struct vmlogrdr_priv_t *priv = dev->driver_data;
552 ssize_t ret = count;
553
554 switch (buf[0]) {
555 case '0':
556 priv->autopurge=0;
557 break;
558 case '1':
559 priv->autopurge=1;
560 break;
561 default:
562 ret = -EINVAL;
563 }
564 return ret;
565}
566
567
568static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400569vmlogrdr_autopurge_show(struct device *dev, struct device_attribute *attr, char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 struct vmlogrdr_priv_t *priv = dev->driver_data;
571 return sprintf(buf, "%u\n", priv->autopurge);
572}
573
574
575static DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
576 vmlogrdr_autopurge_store);
577
578
579static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400580vmlogrdr_purge_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
582 char cp_command[80];
583 char cp_response[80];
584 struct vmlogrdr_priv_t *priv = dev->driver_data;
585
586 if (buf[0] != '1')
587 return -EINVAL;
588
589 memset(cp_command, 0x00, sizeof(cp_command));
590 memset(cp_response, 0x00, sizeof(cp_response));
591
592 /*
593 * The recording command needs to be called with option QID
594 * for guests that have previlege classes A or B.
595 * Other guests will not recognize the command and we have to
596 * issue the same command without the QID parameter.
597 */
598
599 if (recording_class_AB)
600 snprintf(cp_command, sizeof(cp_command),
601 "RECORDING %s PURGE QID * ",
602 priv->recording_name);
603 else
604 snprintf(cp_command, sizeof(cp_command),
605 "RECORDING %s PURGE ",
606 priv->recording_name);
607
608 printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700609 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 printk (KERN_DEBUG "vmlogrdr: recording response: %s",
611 cp_response);
612
613 return count;
614}
615
616
617static DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
618
619
620static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400621vmlogrdr_autorecording_store(struct device *dev, struct device_attribute *attr, const char *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 size_t count) {
623 struct vmlogrdr_priv_t *priv = dev->driver_data;
624 ssize_t ret = count;
625
626 switch (buf[0]) {
627 case '0':
628 priv->autorecording=0;
629 break;
630 case '1':
631 priv->autorecording=1;
632 break;
633 default:
634 ret = -EINVAL;
635 }
636 return ret;
637}
638
639
640static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400641vmlogrdr_autorecording_show(struct device *dev, struct device_attribute *attr, char *buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 struct vmlogrdr_priv_t *priv = dev->driver_data;
643 return sprintf(buf, "%u\n", priv->autorecording);
644}
645
646
647static DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
648 vmlogrdr_autorecording_store);
649
650
651static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400652vmlogrdr_recording_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
654 struct vmlogrdr_priv_t *priv = dev->driver_data;
655 ssize_t ret;
656
657 switch (buf[0]) {
658 case '0':
659 ret = vmlogrdr_recording(priv,0,0);
660 break;
661 case '1':
662 ret = vmlogrdr_recording(priv,1,0);
663 break;
664 default:
665 ret = -EINVAL;
666 }
667 if (ret)
668 return ret;
669 else
670 return count;
671
672}
673
674
675static DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
676
677
678static ssize_t
679vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) {
680
681 char cp_command[] = "QUERY RECORDING ";
682 int len;
683
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700684 cpcmd(cp_command, buf, 4096, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 len = strlen(buf);
686 return len;
687}
688
689
690static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
691 NULL);
692
693static struct attribute *vmlogrdr_attrs[] = {
694 &dev_attr_autopurge.attr,
695 &dev_attr_purge.attr,
696 &dev_attr_autorecording.attr,
697 &dev_attr_recording.attr,
698 NULL,
699};
700
701static struct attribute_group vmlogrdr_attr_group = {
702 .attrs = vmlogrdr_attrs,
703};
704
gregkh@suse.de56b22932005-03-23 10:01:41 -0800705static struct class *vmlogrdr_class;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706static struct device_driver vmlogrdr_driver = {
707 .name = "vmlogrdr",
708 .bus = &iucv_bus,
709};
710
711
712static int
713vmlogrdr_register_driver(void) {
714 int ret;
715
716 ret = driver_register(&vmlogrdr_driver);
717 if (ret) {
718 printk(KERN_ERR "vmlogrdr: failed to register driver.\n");
719 return ret;
720 }
721
722 ret = driver_create_file(&vmlogrdr_driver,
723 &driver_attr_recording_status);
724 if (ret) {
725 printk(KERN_ERR "vmlogrdr: failed to add driver attribute.\n");
726 goto unregdriver;
727 }
728
gregkh@suse.de56b22932005-03-23 10:01:41 -0800729 vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 if (IS_ERR(vmlogrdr_class)) {
731 printk(KERN_ERR "vmlogrdr: failed to create class.\n");
732 ret=PTR_ERR(vmlogrdr_class);
733 vmlogrdr_class=NULL;
734 goto unregattr;
735 }
736 return 0;
737
738unregattr:
739 driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
740unregdriver:
741 driver_unregister(&vmlogrdr_driver);
742 return ret;
743}
744
745
746static void
747vmlogrdr_unregister_driver(void) {
gregkh@suse.de56b22932005-03-23 10:01:41 -0800748 class_destroy(vmlogrdr_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 vmlogrdr_class = NULL;
750 driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
751 driver_unregister(&vmlogrdr_driver);
752 return;
753}
754
755
756static int
757vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) {
758 struct device *dev;
759 int ret;
760
Eric Sesterhenn88abaab2006-03-24 03:15:31 -0800761 dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 if (dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 snprintf(dev->bus_id, BUS_ID_SIZE, "%s",
764 priv->internal_name);
765 dev->bus = &iucv_bus;
766 dev->parent = iucv_root;
767 dev->driver = &vmlogrdr_driver;
768 /*
769 * The release function could be called after the
770 * module has been unloaded. It's _only_ task is to
771 * free the struct. Therefore, we specify kfree()
772 * directly here. (Probably a little bit obfuscating
773 * but legitime ...).
774 */
775 dev->release = (void (*)(struct device *))kfree;
776 } else
777 return -ENOMEM;
778 ret = device_register(dev);
779 if (ret)
780 return ret;
781
782 ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group);
783 if (ret) {
784 device_unregister(dev);
785 return ret;
786 }
gregkh@suse.de56b22932005-03-23 10:01:41 -0800787 priv->class_device = class_device_create(
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 vmlogrdr_class,
Al Virof6b52e82005-10-29 07:06:59 +0100789 NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 MKDEV(vmlogrdr_major, priv->minor_num),
791 dev,
792 "%s", dev->bus_id );
793 if (IS_ERR(priv->class_device)) {
794 ret = PTR_ERR(priv->class_device);
795 priv->class_device=NULL;
796 sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group);
797 device_unregister(dev);
798 return ret;
799 }
800 dev->driver_data = priv;
801 priv->device = dev;
802 return 0;
803}
804
805
806static int
807vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv ) {
gregkh@suse.de56b22932005-03-23 10:01:41 -0800808 class_device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 if (priv->device != NULL) {
810 sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group);
811 device_unregister(priv->device);
812 priv->device=NULL;
813 }
814 return 0;
815}
816
817
818static int
819vmlogrdr_register_cdev(dev_t dev) {
820 int rc = 0;
821 vmlogrdr_cdev = cdev_alloc();
822 if (!vmlogrdr_cdev) {
823 return -ENOMEM;
824 }
825 vmlogrdr_cdev->owner = THIS_MODULE;
826 vmlogrdr_cdev->ops = &vmlogrdr_fops;
827 vmlogrdr_cdev->dev = dev;
828 rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
829 if (!rc)
830 return 0;
831
832 // cleanup: cdev is not fully registered, no cdev_del here!
833 kobject_put(&vmlogrdr_cdev->kobj);
834 vmlogrdr_cdev=NULL;
835 return rc;
836}
837
838
839static void
840vmlogrdr_cleanup(void) {
841 int i;
842 if (vmlogrdr_cdev) {
843 cdev_del(vmlogrdr_cdev);
844 vmlogrdr_cdev=NULL;
845 }
846 for (i=0; i < MAXMINOR; ++i ) {
847 vmlogrdr_unregister_device(&sys_ser[i]);
848 free_page((unsigned long)sys_ser[i].buffer);
849 }
850 vmlogrdr_unregister_driver();
851 if (vmlogrdr_major) {
852 unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
853 vmlogrdr_major=0;
854 }
855}
856
857
858static int
859vmlogrdr_init(void)
860{
861 int rc;
862 int i;
863 dev_t dev;
864
865 if (! MACHINE_IS_VM) {
866 printk (KERN_ERR "vmlogrdr: not running under VM, "
867 "driver not loaded.\n");
868 return -ENODEV;
869 }
870
871 recording_class_AB = vmlogrdr_get_recording_class_AB();
872
873 rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
874 if (rc)
875 return rc;
876 vmlogrdr_major = MAJOR(dev);
877
878 rc=vmlogrdr_register_driver();
879 if (rc)
880 goto cleanup;
881
882 for (i=0; i < MAXMINOR; ++i ) {
883 sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
884 if (!sys_ser[i].buffer) {
885 rc = ENOMEM;
886 break;
887 }
888 sys_ser[i].current_position = sys_ser[i].buffer;
889 rc=vmlogrdr_register_device(&sys_ser[i]);
890 if (rc)
891 break;
892 }
893 if (rc)
894 goto cleanup;
895
896 rc = vmlogrdr_register_cdev(dev);
897 if (rc)
898 goto cleanup;
899 printk (KERN_INFO "vmlogrdr: driver loaded\n");
900 return 0;
901
902cleanup:
903 vmlogrdr_cleanup();
904 printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
905 return rc;
906}
907
908
909static void
910vmlogrdr_exit(void)
911{
912 vmlogrdr_cleanup();
913 printk (KERN_INFO "vmlogrdr: driver unloaded\n");
914 return;
915}
916
917
918module_init(vmlogrdr_init);
919module_exit(vmlogrdr_exit);