blob: 1cc726b98ec8e1893a71caf22a83c41685fb3c3c [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 *
Stefan Weinhuber9f62fa12009-06-16 10:30:38 +02006 * Copyright IBM Corp. 2004, 2009
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * 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 */
Martin Schwidefsky5466c2e2008-12-25 13:39:52 +010013
14#define KMSG_COMPONENT "vmlogrdr"
15#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
16
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/module.h>
18#include <linux/init.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090019#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/errno.h>
21#include <linux/types.h>
22#include <linux/interrupt.h>
23#include <linux/spinlock.h>
24#include <asm/atomic.h>
25#include <asm/uaccess.h>
26#include <asm/cpcmd.h>
27#include <asm/debug.h>
28#include <asm/ebcdic.h>
Martin Schwidefskyc9101c52007-02-08 13:40:41 -080029#include <net/iucv/iucv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/kmod.h>
31#include <linux/cdev.h>
32#include <linux/device.h>
Jonathan Corbet764a4a82008-05-15 10:01:17 -060033#include <linux/smp_lock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/string.h>
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036MODULE_AUTHOR
37 ("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
38 " Stefan Weinhuber (wein@de.ibm.com)");
39MODULE_DESCRIPTION ("Character device driver for reading z/VM "
40 "system service records.");
41MODULE_LICENSE("GPL");
42
43
44/*
45 * The size of the buffer for iucv data transfer is one page,
46 * but in addition to the data we read from iucv we also
47 * place an integer and some characters into that buffer,
48 * so the maximum size for record data is a little less then
49 * one page.
50 */
51#define NET_BUFFER_SIZE (PAGE_SIZE - sizeof(int) - sizeof(FENCE))
52
53/*
54 * The elements that are concurrently accessed by bottom halves are
55 * connection_established, iucv_path_severed, local_interrupt_buffer
56 * and receive_ready. The first three can be protected by
57 * priv_lock. receive_ready is atomic, so it can be incremented and
58 * decremented without holding a lock.
59 * The variable dev_in_use needs to be protected by the lock, since
60 * it's a flag used by open to make sure that the device is opened only
61 * by one user at the same time.
62 */
63struct vmlogrdr_priv_t {
64 char system_service[8];
65 char internal_name[8];
66 char recording_name[8];
Martin Schwidefskyc9101c52007-02-08 13:40:41 -080067 struct iucv_path *path;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 int connection_established;
69 int iucv_path_severed;
Martin Schwidefskyc9101c52007-02-08 13:40:41 -080070 struct iucv_message local_interrupt_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 atomic_t receive_ready;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 int minor_num;
73 char * buffer;
74 char * current_position;
75 int remaining;
76 ulong residual_length;
77 int buffer_free;
78 int dev_in_use; /* 1: already opened, 0: not opened*/
79 spinlock_t priv_lock;
80 struct device *device;
Cornelia Huck7f021ce2007-10-22 12:52:42 +020081 struct device *class_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 int autorecording;
83 int autopurge;
84};
85
86
87/*
88 * File operation structure for vmlogrdr devices
89 */
90static int vmlogrdr_open(struct inode *, struct file *);
91static int vmlogrdr_release(struct inode *, struct file *);
Heiko Carstensd2c993d2006-07-12 16:41:55 +020092static ssize_t vmlogrdr_read (struct file *filp, char __user *data,
93 size_t count, loff_t * ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
Arjan van de Vend54b1fd2007-02-12 00:55:34 -080095static const struct file_operations vmlogrdr_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 .owner = THIS_MODULE,
97 .open = vmlogrdr_open,
98 .release = vmlogrdr_release,
99 .read = vmlogrdr_read,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200100 .llseek = no_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101};
102
103
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800104static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]);
105static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]);
106static void vmlogrdr_iucv_message_pending(struct iucv_path *,
107 struct iucv_message *);
108
109
110static struct iucv_handler vmlogrdr_iucv_handler = {
111 .path_complete = vmlogrdr_iucv_path_complete,
112 .path_severed = vmlogrdr_iucv_path_severed,
113 .message_pending = vmlogrdr_iucv_message_pending,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114};
115
116
Heiko Carstens2b67fc42007-02-05 21:16:47 +0100117static DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
118static DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
120/*
121 * pointer to system service private structure
122 * minor number 0 --> logrec
123 * minor number 1 --> account
124 * minor number 2 --> symptom
125 */
126
127static struct vmlogrdr_priv_t sys_ser[] = {
128 { .system_service = "*LOGREC ",
129 .internal_name = "logrec",
130 .recording_name = "EREP",
131 .minor_num = 0,
132 .buffer_free = 1,
Milind Arun Choudharycb629a02007-04-27 16:02:01 +0200133 .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 .autorecording = 1,
135 .autopurge = 1,
136 },
137 { .system_service = "*ACCOUNT",
138 .internal_name = "account",
139 .recording_name = "ACCOUNT",
140 .minor_num = 1,
141 .buffer_free = 1,
Milind Arun Choudharycb629a02007-04-27 16:02:01 +0200142 .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 .autorecording = 1,
144 .autopurge = 1,
145 },
146 { .system_service = "*SYMPTOM",
147 .internal_name = "symptom",
148 .recording_name = "SYMPTOM",
149 .minor_num = 2,
150 .buffer_free = 1,
Milind Arun Choudharycb629a02007-04-27 16:02:01 +0200151 .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 .autorecording = 1,
153 .autopurge = 1,
154 }
155};
156
157#define MAXMINOR (sizeof(sys_ser)/sizeof(struct vmlogrdr_priv_t))
158
159static char FENCE[] = {"EOR"};
160static int vmlogrdr_major = 0;
161static struct cdev *vmlogrdr_cdev = NULL;
162static int recording_class_AB;
163
164
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800165static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800167 struct vmlogrdr_priv_t * logptr = path->private;
168
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 spin_lock(&logptr->priv_lock);
170 logptr->connection_established = 1;
171 spin_unlock(&logptr->priv_lock);
172 wake_up(&conn_wait_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800176static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177{
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800178 struct vmlogrdr_priv_t * logptr = path->private;
179 u8 reason = (u8) ipuser[8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Martin Schwidefsky5466c2e2008-12-25 13:39:52 +0100181 pr_err("vmlogrdr: connection severed with reason %i\n", reason);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800183 iucv_path_sever(path, NULL);
184 kfree(path);
185 logptr->path = NULL;
186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 spin_lock(&logptr->priv_lock);
188 logptr->connection_established = 0;
189 logptr->iucv_path_severed = 1;
190 spin_unlock(&logptr->priv_lock);
191
192 wake_up(&conn_wait_queue);
193 /* just in case we're sleeping waiting for a record */
194 wake_up_interruptible(&read_wait_queue);
195}
196
197
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800198static void vmlogrdr_iucv_message_pending(struct iucv_path *path,
199 struct iucv_message *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200{
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800201 struct vmlogrdr_priv_t * logptr = path->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203 /*
204 * This function is the bottom half so it should be quick.
205 * Copy the external interrupt data into our local eib and increment
206 * the usage count
207 */
208 spin_lock(&logptr->priv_lock);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800209 memcpy(&logptr->local_interrupt_buffer, msg, sizeof(*msg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 atomic_inc(&logptr->receive_ready);
211 spin_unlock(&logptr->priv_lock);
212 wake_up_interruptible(&read_wait_queue);
213}
214
215
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800216static int vmlogrdr_get_recording_class_AB(void)
217{
Joe Perchesbf2106a2010-10-25 16:10:20 +0200218 static const char cp_command[] = "QUERY COMMAND RECORDING ";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 char cp_response[80];
220 char *tail;
221 int len,i;
222
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700223 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 len = strnlen(cp_response,sizeof(cp_response));
225 // now the parsing
226 tail=strnchr(cp_response,len,'=');
227 if (!tail)
228 return 0;
229 tail++;
230 if (!strncmp("ANY",tail,3))
231 return 1;
232 if (!strncmp("NONE",tail,4))
233 return 0;
234 /*
235 * expect comma separated list of classes here, if one of them
236 * is A or B return 1 otherwise 0
237 */
238 for (i=tail-cp_response; i<len; i++)
239 if ( cp_response[i]=='A' || cp_response[i]=='B' )
240 return 1;
241 return 0;
242}
243
244
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800245static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,
246 int action, int purge)
247{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 char cp_command[80];
250 char cp_response[160];
251 char *onoff, *qid_string;
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100252 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100254 onoff = ((action == 1) ? "ON" : "OFF");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 qid_string = ((recording_class_AB == 1) ? " QID * " : "");
256
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100257 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 * The recording commands needs to be called with option QID
259 * for guests that have previlege classes A or B.
260 * Purging has to be done as separate step, because recording
261 * can't be switched on as long as records are on the queue.
262 * Doing both at the same time doesn't work.
263 */
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100264 if (purge && (action == 1)) {
265 memset(cp_command, 0x00, sizeof(cp_command));
266 memset(cp_response, 0x00, sizeof(cp_response));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 snprintf(cp_command, sizeof(cp_command),
268 "RECORDING %s PURGE %s",
269 logptr->recording_name,
270 qid_string);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700271 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 }
273
274 memset(cp_command, 0x00, sizeof(cp_command));
275 memset(cp_response, 0x00, sizeof(cp_response));
276 snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
277 logptr->recording_name,
278 onoff,
279 qid_string);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700280 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 /* The recording command will usually answer with 'Command complete'
282 * on success, but when the specific service was never connected
283 * before then there might be an additional informational message
284 * 'HCPCRC8072I Recording entry not found' before the
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100285 * 'Command complete'. So I use strstr rather then the strncmp.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 */
287 if (strstr(cp_response,"Command complete"))
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100288 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 else
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100290 rc = -EIO;
291 /*
292 * If we turn recording off, we have to purge any remaining records
293 * afterwards, as a large number of queued records may impact z/VM
294 * performance.
295 */
296 if (purge && (action == 0)) {
297 memset(cp_command, 0x00, sizeof(cp_command));
298 memset(cp_response, 0x00, sizeof(cp_response));
299 snprintf(cp_command, sizeof(cp_command),
300 "RECORDING %s PURGE %s",
301 logptr->recording_name,
302 qid_string);
303 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
304 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Stefan Weinhuberca768b62010-11-10 10:05:54 +0100306 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800310static int vmlogrdr_open (struct inode *inode, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311{
312 int dev_num = 0;
313 struct vmlogrdr_priv_t * logptr = NULL;
314 int connect_rc = 0;
315 int ret;
316
317 dev_num = iminor(inode);
318 if (dev_num > MAXMINOR)
319 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 logptr = &sys_ser[dev_num];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
322 /*
323 * only allow for blocking reads to be open
324 */
325 if (filp->f_flags & O_NONBLOCK)
326 return -ENOSYS;
327
328 /* Besure this device hasn't already been opened */
329 spin_lock_bh(&logptr->priv_lock);
330 if (logptr->dev_in_use) {
331 spin_unlock_bh(&logptr->priv_lock);
332 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800334 logptr->dev_in_use = 1;
335 logptr->connection_established = 0;
336 logptr->iucv_path_severed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 atomic_set(&logptr->receive_ready, 0);
338 logptr->buffer_free = 1;
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800339 spin_unlock_bh(&logptr->priv_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
341 /* set the file options */
342 filp->private_data = logptr;
343 filp->f_op = &vmlogrdr_fops;
344
345 /* start recording for this service*/
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800346 if (logptr->autorecording) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800348 if (ret)
Martin Schwidefsky5466c2e2008-12-25 13:39:52 +0100349 pr_warning("vmlogrdr: failed to start "
350 "recording automatically\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 }
352
353 /* create connection to the system service */
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800354 logptr->path = iucv_path_alloc(10, 0, GFP_KERNEL);
355 if (!logptr->path)
356 goto out_dev;
357 connect_rc = iucv_path_connect(logptr->path, &vmlogrdr_iucv_handler,
358 logptr->system_service, NULL, NULL,
359 logptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 if (connect_rc) {
Martin Schwidefsky5466c2e2008-12-25 13:39:52 +0100361 pr_err("vmlogrdr: iucv connection to %s "
362 "failed with rc %i \n",
363 logptr->system_service, connect_rc);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800364 goto out_path;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
366
367 /* We've issued the connect and now we must wait for a
368 * ConnectionComplete or ConnectinSevered Interrupt
369 * before we can continue to process.
370 */
371 wait_event(conn_wait_queue, (logptr->connection_established)
372 || (logptr->iucv_path_severed));
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800373 if (logptr->iucv_path_severed)
374 goto out_record;
Martin Schwidefsky3b47f9d2009-12-07 12:52:23 +0100375 nonseekable_open(inode, filp);
376 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800378out_record:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 if (logptr->autorecording)
380 vmlogrdr_recording(logptr,0,logptr->autopurge);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800381out_path:
382 kfree(logptr->path); /* kfree(NULL) is ok. */
383 logptr->path = NULL;
384out_dev:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 logptr->dev_in_use = 0;
386 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387}
388
389
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800390static int vmlogrdr_release (struct inode *inode, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391{
392 int ret;
393
394 struct vmlogrdr_priv_t * logptr = filp->private_data;
395
Ursula Braun66b494a2007-04-27 16:01:52 +0200396 iucv_path_sever(logptr->path, NULL);
397 kfree(logptr->path);
398 logptr->path = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 if (logptr->autorecording) {
400 ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
401 if (ret)
Martin Schwidefsky5466c2e2008-12-25 13:39:52 +0100402 pr_warning("vmlogrdr: failed to stop "
403 "recording automatically\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 }
405 logptr->dev_in_use = 0;
406
407 return 0;
408}
409
410
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800411static int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv)
412{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 int rc, *temp;
414 /* we need to keep track of two data sizes here:
415 * The number of bytes we need to receive from iucv and
416 * the total number of bytes we actually write into the buffer.
417 */
418 int user_data_count, iucv_data_count;
419 char * buffer;
420
421 if (atomic_read(&priv->receive_ready)) {
422 spin_lock_bh(&priv->priv_lock);
423 if (priv->residual_length){
424 /* receive second half of a record */
425 iucv_data_count = priv->residual_length;
426 user_data_count = 0;
427 buffer = priv->buffer;
428 } else {
429 /* receive a new record:
430 * We need to return the total length of the record
431 * + size of FENCE in the first 4 bytes of the buffer.
432 */
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800433 iucv_data_count = priv->local_interrupt_buffer.length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 user_data_count = sizeof(int);
435 temp = (int*)priv->buffer;
436 *temp= iucv_data_count + sizeof(FENCE);
437 buffer = priv->buffer + sizeof(int);
438 }
439 /*
Frederik Schwarzer025dfda2008-10-16 19:02:37 +0200440 * If the record is bigger than our buffer, we receive only
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 * a part of it. We can get the rest later.
442 */
443 if (iucv_data_count > NET_BUFFER_SIZE)
444 iucv_data_count = NET_BUFFER_SIZE;
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800445 rc = iucv_message_receive(priv->path,
446 &priv->local_interrupt_buffer,
447 0, buffer, iucv_data_count,
448 &priv->residual_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 spin_unlock_bh(&priv->priv_lock);
Frederik Schwarzer025dfda2008-10-16 19:02:37 +0200450 /* An rc of 5 indicates that the record was bigger than
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 * the buffer, which is OK for us. A 9 indicates that the
452 * record was purged befor we could receive it.
453 */
454 if (rc == 5)
455 rc = 0;
456 if (rc == 9)
457 atomic_set(&priv->receive_ready, 0);
458 } else {
459 rc = 1;
460 }
461 if (!rc) {
462 priv->buffer_free = 0;
463 user_data_count += iucv_data_count;
464 priv->current_position = priv->buffer;
465 if (priv->residual_length == 0){
466 /* the whole record has been captured,
467 * now add the fence */
468 atomic_dec(&priv->receive_ready);
469 buffer = priv->buffer + user_data_count;
470 memcpy(buffer, FENCE, sizeof(FENCE));
471 user_data_count += sizeof(FENCE);
472 }
473 priv->remaining = user_data_count;
474 }
475
476 return rc;
477}
478
479
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800480static ssize_t vmlogrdr_read(struct file *filp, char __user *data,
481 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
483 int rc;
484 struct vmlogrdr_priv_t * priv = filp->private_data;
485
486 while (priv->buffer_free) {
487 rc = vmlogrdr_receive_data(priv);
488 if (rc) {
489 rc = wait_event_interruptible(read_wait_queue,
490 atomic_read(&priv->receive_ready));
491 if (rc)
492 return rc;
493 }
494 }
495 /* copy only up to end of record */
496 if (count > priv->remaining)
497 count = priv->remaining;
498
499 if (copy_to_user(data, priv->current_position, count))
500 return -EFAULT;
501
502 *ppos += count;
503 priv->current_position += count;
504 priv->remaining -= count;
505
506 /* if all data has been transferred, set buffer free */
507 if (priv->remaining == 0)
508 priv->buffer_free = 1;
509
510 return count;
511}
512
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800513static ssize_t vmlogrdr_autopurge_store(struct device * dev,
514 struct device_attribute *attr,
515 const char * buf, size_t count)
516{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -0700517 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 ssize_t ret = count;
519
520 switch (buf[0]) {
521 case '0':
522 priv->autopurge=0;
523 break;
524 case '1':
525 priv->autopurge=1;
526 break;
527 default:
528 ret = -EINVAL;
529 }
530 return ret;
531}
532
533
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800534static ssize_t vmlogrdr_autopurge_show(struct device *dev,
535 struct device_attribute *attr,
536 char *buf)
537{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -0700538 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 return sprintf(buf, "%u\n", priv->autopurge);
540}
541
542
543static DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
544 vmlogrdr_autopurge_store);
545
546
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800547static ssize_t vmlogrdr_purge_store(struct device * dev,
548 struct device_attribute *attr,
549 const char * buf, size_t count)
550{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
552 char cp_command[80];
553 char cp_response[80];
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -0700554 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556 if (buf[0] != '1')
557 return -EINVAL;
558
559 memset(cp_command, 0x00, sizeof(cp_command));
560 memset(cp_response, 0x00, sizeof(cp_response));
561
562 /*
563 * The recording command needs to be called with option QID
564 * for guests that have previlege classes A or B.
565 * Other guests will not recognize the command and we have to
566 * issue the same command without the QID parameter.
567 */
568
569 if (recording_class_AB)
570 snprintf(cp_command, sizeof(cp_command),
571 "RECORDING %s PURGE QID * ",
572 priv->recording_name);
573 else
574 snprintf(cp_command, sizeof(cp_command),
575 "RECORDING %s PURGE ",
576 priv->recording_name);
577
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700578 cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
580 return count;
581}
582
583
584static DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
585
586
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800587static ssize_t vmlogrdr_autorecording_store(struct device *dev,
588 struct device_attribute *attr,
589 const char *buf, size_t count)
590{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -0700591 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 ssize_t ret = count;
593
594 switch (buf[0]) {
595 case '0':
596 priv->autorecording=0;
597 break;
598 case '1':
599 priv->autorecording=1;
600 break;
601 default:
602 ret = -EINVAL;
603 }
604 return ret;
605}
606
607
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800608static ssize_t vmlogrdr_autorecording_show(struct device *dev,
609 struct device_attribute *attr,
610 char *buf)
611{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -0700612 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 return sprintf(buf, "%u\n", priv->autorecording);
614}
615
616
617static DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
618 vmlogrdr_autorecording_store);
619
620
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800621static ssize_t vmlogrdr_recording_store(struct device * dev,
622 struct device_attribute *attr,
623 const char * buf, size_t count)
624{
Greg Kroah-Hartmandff59b62009-05-04 12:40:54 -0700625 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 ssize_t ret;
627
628 switch (buf[0]) {
629 case '0':
630 ret = vmlogrdr_recording(priv,0,0);
631 break;
632 case '1':
633 ret = vmlogrdr_recording(priv,1,0);
634 break;
635 default:
636 ret = -EINVAL;
637 }
638 if (ret)
639 return ret;
640 else
641 return count;
642
643}
644
645
646static DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
647
648
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800649static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,
650 char *buf)
651{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Joe Perchesbf2106a2010-10-25 16:10:20 +0200653 static const char cp_command[] = "QUERY RECORDING ";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 int len;
655
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700656 cpcmd(cp_command, buf, 4096, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 len = strlen(buf);
658 return len;
659}
660
661
662static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
663 NULL);
664
665static struct attribute *vmlogrdr_attrs[] = {
666 &dev_attr_autopurge.attr,
667 &dev_attr_purge.attr,
668 &dev_attr_autorecording.attr,
669 &dev_attr_recording.attr,
670 NULL,
671};
672
Stefan Weinhuber9f62fa12009-06-16 10:30:38 +0200673static int vmlogrdr_pm_prepare(struct device *dev)
674{
675 int rc;
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +0200676 struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
Stefan Weinhuber9f62fa12009-06-16 10:30:38 +0200677
678 rc = 0;
679 if (priv) {
680 spin_lock_bh(&priv->priv_lock);
681 if (priv->dev_in_use)
682 rc = -EBUSY;
683 spin_unlock_bh(&priv->priv_lock);
684 }
685 if (rc)
686 pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n",
687 dev_name(dev));
688 return rc;
689}
690
691
Alexey Dobriyan47145212009-12-14 18:00:08 -0800692static const struct dev_pm_ops vmlogrdr_pm_ops = {
Stefan Weinhuber9f62fa12009-06-16 10:30:38 +0200693 .prepare = vmlogrdr_pm_prepare,
694};
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696static struct attribute_group vmlogrdr_attr_group = {
697 .attrs = vmlogrdr_attrs,
698};
699
gregkh@suse.de56b22932005-03-23 10:01:41 -0800700static struct class *vmlogrdr_class;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701static struct device_driver vmlogrdr_driver = {
702 .name = "vmlogrdr",
703 .bus = &iucv_bus,
Stefan Weinhuber9f62fa12009-06-16 10:30:38 +0200704 .pm = &vmlogrdr_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705};
706
707
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800708static int vmlogrdr_register_driver(void)
709{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 int ret;
711
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800712 /* Register with iucv driver */
713 ret = iucv_register(&vmlogrdr_iucv_handler, 1);
Martin Schwidefsky2f6f2522008-07-14 09:59:37 +0200714 if (ret)
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800715 goto out;
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800716
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 ret = driver_register(&vmlogrdr_driver);
Martin Schwidefsky2f6f2522008-07-14 09:59:37 +0200718 if (ret)
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800719 goto out_iucv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
721 ret = driver_create_file(&vmlogrdr_driver,
722 &driver_attr_recording_status);
Martin Schwidefsky2f6f2522008-07-14 09:59:37 +0200723 if (ret)
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800724 goto out_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
gregkh@suse.de56b22932005-03-23 10:01:41 -0800726 vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 if (IS_ERR(vmlogrdr_class)) {
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800728 ret = PTR_ERR(vmlogrdr_class);
729 vmlogrdr_class = NULL;
730 goto out_attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 }
732 return 0;
733
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800734out_attr:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800736out_driver:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 driver_unregister(&vmlogrdr_driver);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800738out_iucv:
739 iucv_unregister(&vmlogrdr_iucv_handler, 1);
740out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 return ret;
742}
743
744
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800745static void vmlogrdr_unregister_driver(void)
746{
gregkh@suse.de56b22932005-03-23 10:01:41 -0800747 class_destroy(vmlogrdr_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 vmlogrdr_class = NULL;
749 driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
750 driver_unregister(&vmlogrdr_driver);
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800751 iucv_unregister(&vmlogrdr_iucv_handler, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752}
753
754
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800755static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
756{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 struct device *dev;
758 int ret;
759
Eric Sesterhenn88abaab2006-03-24 03:15:31 -0800760 dev = kzalloc(sizeof(struct device), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 if (dev) {
Cornelia Huck1bf5b282008-10-10 21:33:10 +0200762 dev_set_name(dev, priv->internal_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 dev->bus = &iucv_bus;
764 dev->parent = iucv_root;
765 dev->driver = &vmlogrdr_driver;
Martin Schwidefsky4f0076f2009-06-22 12:08:19 +0200766 dev_set_drvdata(dev, priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 /*
768 * The release function could be called after the
769 * module has been unloaded. It's _only_ task is to
770 * free the struct. Therefore, we specify kfree()
771 * directly here. (Probably a little bit obfuscating
772 * but legitime ...).
773 */
774 dev->release = (void (*)(struct device *))kfree;
775 } else
776 return -ENOMEM;
777 ret = device_register(dev);
Sebastian Ottc6304932009-09-11 10:28:38 +0200778 if (ret) {
779 put_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 return ret;
Sebastian Ottc6304932009-09-11 10:28:38 +0200781 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group);
784 if (ret) {
785 device_unregister(dev);
786 return ret;
787 }
Greg Kroah-Hartmanea9e42f2008-07-21 20:03:34 -0700788 priv->class_device = device_create(vmlogrdr_class, dev,
789 MKDEV(vmlogrdr_major,
790 priv->minor_num),
791 priv, "%s", dev_name(dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if (IS_ERR(priv->class_device)) {
793 ret = PTR_ERR(priv->class_device);
794 priv->class_device=NULL;
795 sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group);
796 device_unregister(dev);
797 return ret;
798 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 priv->device = dev;
800 return 0;
801}
802
803
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800804static int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv)
805{
Cornelia Huck7f021ce2007-10-22 12:52:42 +0200806 device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 if (priv->device != NULL) {
808 sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group);
809 device_unregister(priv->device);
810 priv->device=NULL;
811 }
812 return 0;
813}
814
815
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800816static int vmlogrdr_register_cdev(dev_t dev)
817{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 int rc = 0;
819 vmlogrdr_cdev = cdev_alloc();
820 if (!vmlogrdr_cdev) {
821 return -ENOMEM;
822 }
823 vmlogrdr_cdev->owner = THIS_MODULE;
824 vmlogrdr_cdev->ops = &vmlogrdr_fops;
825 vmlogrdr_cdev->dev = dev;
826 rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
827 if (!rc)
828 return 0;
829
830 // cleanup: cdev is not fully registered, no cdev_del here!
831 kobject_put(&vmlogrdr_cdev->kobj);
832 vmlogrdr_cdev=NULL;
833 return rc;
834}
835
836
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800837static void vmlogrdr_cleanup(void)
838{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 int i;
Martin Schwidefskyc9101c52007-02-08 13:40:41 -0800840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 if (vmlogrdr_cdev) {
842 cdev_del(vmlogrdr_cdev);
843 vmlogrdr_cdev=NULL;
844 }
845 for (i=0; i < MAXMINOR; ++i ) {
846 vmlogrdr_unregister_device(&sys_ser[i]);
847 free_page((unsigned long)sys_ser[i].buffer);
848 }
849 vmlogrdr_unregister_driver();
850 if (vmlogrdr_major) {
851 unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
852 vmlogrdr_major=0;
853 }
854}
855
856
Christian Borntraegerf60d8912007-07-10 11:24:22 +0200857static int __init vmlogrdr_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858{
859 int rc;
860 int i;
861 dev_t dev;
862
863 if (! MACHINE_IS_VM) {
Martin Schwidefsky5466c2e2008-12-25 13:39:52 +0100864 pr_err("not running under VM, driver not loaded.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 return -ENODEV;
866 }
867
868 recording_class_AB = vmlogrdr_get_recording_class_AB();
869
870 rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
871 if (rc)
872 return rc;
873 vmlogrdr_major = MAJOR(dev);
874
875 rc=vmlogrdr_register_driver();
876 if (rc)
877 goto cleanup;
878
879 for (i=0; i < MAXMINOR; ++i ) {
880 sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
881 if (!sys_ser[i].buffer) {
Marcin Slusarz3cb2cea2008-05-15 16:52:32 +0200882 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 break;
884 }
885 sys_ser[i].current_position = sys_ser[i].buffer;
886 rc=vmlogrdr_register_device(&sys_ser[i]);
887 if (rc)
888 break;
889 }
890 if (rc)
891 goto cleanup;
892
893 rc = vmlogrdr_register_cdev(dev);
894 if (rc)
895 goto cleanup;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 return 0;
897
898cleanup:
899 vmlogrdr_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 return rc;
901}
902
903
Christian Borntraegerf60d8912007-07-10 11:24:22 +0200904static void __exit vmlogrdr_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905{
906 vmlogrdr_cleanup();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return;
908}
909
910
911module_init(vmlogrdr_init);
912module_exit(vmlogrdr_exit);