vmlogrdr.c 21.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 *	character device driver for reading z/VM system service records
 *
 *
5
 *	Copyright IBM Corp. 2004, 2009
L
Linus Torvalds 已提交
6 7 8 9 10 11
 *	character device driver for reading z/VM system service records,
 *	Version 1.0
 *	Author(s): Xenia Tkatschow <xenia@us.ibm.com>
 *		   Stefan Weinhuber <wein@de.ibm.com>
 *
 */
12 13 14 15

#define KMSG_COMPONENT "vmlogrdr"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

L
Linus Torvalds 已提交
16 17
#include <linux/module.h>
#include <linux/init.h>
18
#include <linux/slab.h>
L
Linus Torvalds 已提交
19 20 21 22
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
A
Arun Sharma 已提交
23
#include <linux/atomic.h>
L
Linus Torvalds 已提交
24 25 26 27
#include <asm/uaccess.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
28
#include <net/iucv/iucv.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
#include <linux/kmod.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/string.h>

MODULE_AUTHOR
	("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
	 "                            Stefan Weinhuber (wein@de.ibm.com)");
MODULE_DESCRIPTION ("Character device driver for reading z/VM "
		    "system service records.");
MODULE_LICENSE("GPL");


/*
 * The size of the buffer for iucv data transfer is one page,
 * but in addition to the data we read from iucv we also
 * place an integer and some characters into that buffer,
 * so the maximum size for record data is a little less then
 * one page.
 */
#define NET_BUFFER_SIZE	(PAGE_SIZE - sizeof(int) - sizeof(FENCE))

/*
 * The elements that are concurrently accessed by bottom halves are
 * connection_established, iucv_path_severed, local_interrupt_buffer
 * and receive_ready. The first three can be protected by
 * priv_lock.  receive_ready is atomic, so it can be incremented and
 * decremented without holding a lock.
 * The variable dev_in_use needs to be protected by the lock, since
 * it's a flag used by open to make sure that the device is opened only
 * by one user at the same time.
 */
struct vmlogrdr_priv_t {
	char system_service[8];
	char internal_name[8];
	char recording_name[8];
65
	struct iucv_path *path;
L
Linus Torvalds 已提交
66 67
	int connection_established;
	int iucv_path_severed;
68
	struct iucv_message local_interrupt_buffer;
L
Linus Torvalds 已提交
69 70 71 72 73 74 75 76 77 78
	atomic_t receive_ready;
	int minor_num;
	char * buffer;
	char * current_position;
	int remaining;
	ulong residual_length;
	int buffer_free;
	int dev_in_use; /* 1: already opened, 0: not opened*/
	spinlock_t priv_lock;
	struct device  *device;
79
	struct device  *class_device;
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89
	int autorecording;
	int autopurge;
};


/*
 * File operation structure for vmlogrdr devices
 */
static int vmlogrdr_open(struct inode *, struct file *);
static int vmlogrdr_release(struct inode *, struct file *);
H
Heiko Carstens 已提交
90 91
static ssize_t vmlogrdr_read (struct file *filp, char __user *data,
			      size_t count, loff_t * ppos);
L
Linus Torvalds 已提交
92

93
static const struct file_operations vmlogrdr_fops = {
L
Linus Torvalds 已提交
94 95 96 97
	.owner   = THIS_MODULE,
	.open    = vmlogrdr_open,
	.release = vmlogrdr_release,
	.read    = vmlogrdr_read,
98
	.llseek  = no_llseek,
L
Linus Torvalds 已提交
99 100 101
};


102 103
static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser);
static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser);
104 105
static void vmlogrdr_iucv_message_pending(struct iucv_path *,
					  struct iucv_message *);
L
Linus Torvalds 已提交
106 107


108 109 110 111
static struct iucv_handler vmlogrdr_iucv_handler = {
	.path_complete	 = vmlogrdr_iucv_path_complete,
	.path_severed	 = vmlogrdr_iucv_path_severed,
	.message_pending = vmlogrdr_iucv_message_pending,
L
Linus Torvalds 已提交
112 113 114
};


115 116
static DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
static DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
L
Linus Torvalds 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130

/*
 * pointer to system service private structure
 * minor number 0 --> logrec
 * minor number 1 --> account
 * minor number 2 --> symptom
 */

static struct vmlogrdr_priv_t sys_ser[] = {
	{ .system_service = "*LOGREC ",
	  .internal_name  = "logrec",
	  .recording_name = "EREP",
	  .minor_num      = 0,
	  .buffer_free    = 1,
131
	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
L
Linus Torvalds 已提交
132 133 134 135 136 137 138 139
	  .autorecording  = 1,
	  .autopurge      = 1,
	},
	{ .system_service = "*ACCOUNT",
	  .internal_name  = "account",
	  .recording_name = "ACCOUNT",
	  .minor_num      = 1,
	  .buffer_free    = 1,
140
	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
L
Linus Torvalds 已提交
141 142 143 144 145 146 147 148
	  .autorecording  = 1,
	  .autopurge      = 1,
	},
	{ .system_service = "*SYMPTOM",
	  .internal_name  = "symptom",
	  .recording_name = "SYMPTOM",
	  .minor_num      = 2,
	  .buffer_free    = 1,
149
	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162
	  .autorecording  = 1,
	  .autopurge      = 1,
	}
};

#define MAXMINOR  (sizeof(sys_ser)/sizeof(struct vmlogrdr_priv_t))

static char FENCE[] = {"EOR"};
static int vmlogrdr_major = 0;
static struct cdev  *vmlogrdr_cdev = NULL;
static int recording_class_AB;


163
static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
L
Linus Torvalds 已提交
164
{
165 166
	struct vmlogrdr_priv_t * logptr = path->private;

L
Linus Torvalds 已提交
167 168 169 170 171 172 173
	spin_lock(&logptr->priv_lock);
	logptr->connection_established = 1;
	spin_unlock(&logptr->priv_lock);
	wake_up(&conn_wait_queue);
}


174
static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
L
Linus Torvalds 已提交
175
{
176 177
	struct vmlogrdr_priv_t * logptr = path->private;
	u8 reason = (u8) ipuser[8];
L
Linus Torvalds 已提交
178

179
	pr_err("vmlogrdr: connection severed with reason %i\n", reason);
L
Linus Torvalds 已提交
180

181 182 183 184
	iucv_path_sever(path, NULL);
	kfree(path);
	logptr->path = NULL;

L
Linus Torvalds 已提交
185 186 187 188 189 190 191 192 193 194 195
	spin_lock(&logptr->priv_lock);
	logptr->connection_established = 0;
	logptr->iucv_path_severed = 1;
	spin_unlock(&logptr->priv_lock);

	wake_up(&conn_wait_queue);
	/* just in case we're sleeping waiting for a record */
	wake_up_interruptible(&read_wait_queue);
}


196 197
static void vmlogrdr_iucv_message_pending(struct iucv_path *path,
					  struct iucv_message *msg)
L
Linus Torvalds 已提交
198
{
199
	struct vmlogrdr_priv_t * logptr = path->private;
L
Linus Torvalds 已提交
200 201 202 203 204 205 206

	/*
	 * This function is the bottom half so it should be quick.
	 * Copy the external interrupt data into our local eib and increment
	 * the usage count
	 */
	spin_lock(&logptr->priv_lock);
207
	memcpy(&logptr->local_interrupt_buffer, msg, sizeof(*msg));
L
Linus Torvalds 已提交
208 209 210 211 212 213
	atomic_inc(&logptr->receive_ready);
	spin_unlock(&logptr->priv_lock);
	wake_up_interruptible(&read_wait_queue);
}


214 215
static int vmlogrdr_get_recording_class_AB(void)
{
216
	static const char cp_command[] = "QUERY COMMAND RECORDING ";
L
Linus Torvalds 已提交
217 218 219 220
	char cp_response[80];
	char *tail;
	int len,i;

221
	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
L
Linus Torvalds 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	len = strnlen(cp_response,sizeof(cp_response));
	// now the parsing
	tail=strnchr(cp_response,len,'=');
	if (!tail)
		return 0;
	tail++;
	if (!strncmp("ANY",tail,3))
		return 1;
	if (!strncmp("NONE",tail,4))
		return 0;
	/*
	 * expect comma separated list of classes here, if one of them
	 * is A or B return 1 otherwise 0
	 */
        for (i=tail-cp_response; i<len; i++)
		if ( cp_response[i]=='A' || cp_response[i]=='B' )
			return 1;
	return 0;
}


243 244 245
static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,
			      int action, int purge)
{
L
Linus Torvalds 已提交
246 247 248 249

	char cp_command[80];
	char cp_response[160];
	char *onoff, *qid_string;
250
	int rc;
L
Linus Torvalds 已提交
251

252
	onoff = ((action == 1) ? "ON" : "OFF");
L
Linus Torvalds 已提交
253 254
	qid_string = ((recording_class_AB == 1) ? " QID * " : "");

255
	/*
L
Linus Torvalds 已提交
256 257 258 259 260 261
	 * The recording commands needs to be called with option QID
	 * for guests that have previlege classes A or B.
	 * Purging has to be done as separate step, because recording
	 * can't be switched on as long as records are on the queue.
	 * Doing both at the same time doesn't work.
	 */
262 263 264
	if (purge && (action == 1)) {
		memset(cp_command, 0x00, sizeof(cp_command));
		memset(cp_response, 0x00, sizeof(cp_response));
L
Linus Torvalds 已提交
265 266 267 268
		snprintf(cp_command, sizeof(cp_command),
			 "RECORDING %s PURGE %s",
			 logptr->recording_name,
			 qid_string);
269
		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
L
Linus Torvalds 已提交
270 271 272 273 274 275 276 277
	}

	memset(cp_command, 0x00, sizeof(cp_command));
	memset(cp_response, 0x00, sizeof(cp_response));
	snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
		logptr->recording_name,
		onoff,
		qid_string);
278
	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
L
Linus Torvalds 已提交
279 280 281 282
	/* The recording command will usually answer with 'Command complete'
	 * on success, but when the specific service was never connected
	 * before then there might be an additional informational message
	 * 'HCPCRC8072I Recording entry not found' before the
283
	 * 'Command complete'. So I use strstr rather then the strncmp.
L
Linus Torvalds 已提交
284 285
	 */
	if (strstr(cp_response,"Command complete"))
286
		rc = 0;
L
Linus Torvalds 已提交
287
	else
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
		rc = -EIO;
	/*
	 * If we turn recording off, we have to purge any remaining records
	 * afterwards, as a large number of queued records may impact z/VM
	 * performance.
	 */
	if (purge && (action == 0)) {
		memset(cp_command, 0x00, sizeof(cp_command));
		memset(cp_response, 0x00, sizeof(cp_response));
		snprintf(cp_command, sizeof(cp_command),
			 "RECORDING %s PURGE %s",
			 logptr->recording_name,
			 qid_string);
		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
	}
L
Linus Torvalds 已提交
303

304
	return rc;
L
Linus Torvalds 已提交
305 306 307
}


308
static int vmlogrdr_open (struct inode *inode, struct file *filp)
L
Linus Torvalds 已提交
309 310 311 312 313 314 315
{
	int dev_num = 0;
	struct vmlogrdr_priv_t * logptr = NULL;
	int connect_rc = 0;
	int ret;

	dev_num = iminor(inode);
316
	if (dev_num >= MAXMINOR)
L
Linus Torvalds 已提交
317 318 319 320 321 322 323
		return -ENODEV;
	logptr = &sys_ser[dev_num];

	/*
	 * only allow for blocking reads to be open
	 */
	if (filp->f_flags & O_NONBLOCK)
324
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
325 326 327 328 329 330 331

	/* Besure this device hasn't already been opened */
	spin_lock_bh(&logptr->priv_lock);
	if (logptr->dev_in_use)	{
		spin_unlock_bh(&logptr->priv_lock);
		return -EBUSY;
	}
332 333 334
	logptr->dev_in_use = 1;
	logptr->connection_established = 0;
	logptr->iucv_path_severed = 0;
L
Linus Torvalds 已提交
335 336
	atomic_set(&logptr->receive_ready, 0);
	logptr->buffer_free = 1;
337
	spin_unlock_bh(&logptr->priv_lock);
L
Linus Torvalds 已提交
338 339 340 341 342

	/* set the file options */
	filp->private_data = logptr;

	/* start recording for this service*/
343
	if (logptr->autorecording) {
L
Linus Torvalds 已提交
344
		ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
345
		if (ret)
346
			pr_warn("vmlogrdr: failed to start recording automatically\n");
L
Linus Torvalds 已提交
347 348 349
	}

	/* create connection to the system service */
350 351 352 353 354 355
	logptr->path = iucv_path_alloc(10, 0, GFP_KERNEL);
	if (!logptr->path)
		goto out_dev;
	connect_rc = iucv_path_connect(logptr->path, &vmlogrdr_iucv_handler,
				       logptr->system_service, NULL, NULL,
				       logptr);
L
Linus Torvalds 已提交
356
	if (connect_rc) {
357 358 359
		pr_err("vmlogrdr: iucv connection to %s "
		       "failed with rc %i \n",
		       logptr->system_service, connect_rc);
360
		goto out_path;
L
Linus Torvalds 已提交
361 362 363 364 365 366 367 368
	}

	/* We've issued the connect and now we must wait for a
	 * ConnectionComplete or ConnectinSevered Interrupt
	 * before we can continue to process.
	 */
	wait_event(conn_wait_queue, (logptr->connection_established)
		   || (logptr->iucv_path_severed));
369 370
	if (logptr->iucv_path_severed)
		goto out_record;
M
Martin Schwidefsky 已提交
371 372
	nonseekable_open(inode, filp);
	return 0;
L
Linus Torvalds 已提交
373

374
out_record:
L
Linus Torvalds 已提交
375 376
	if (logptr->autorecording)
		vmlogrdr_recording(logptr,0,logptr->autopurge);
377 378 379 380
out_path:
	kfree(logptr->path);	/* kfree(NULL) is ok. */
	logptr->path = NULL;
out_dev:
L
Linus Torvalds 已提交
381 382 383 384 385
	logptr->dev_in_use = 0;
	return -EIO;
}


386
static int vmlogrdr_release (struct inode *inode, struct file *filp)
L
Linus Torvalds 已提交
387 388 389 390 391
{
	int ret;

	struct vmlogrdr_priv_t * logptr = filp->private_data;

392 393 394
	iucv_path_sever(logptr->path, NULL);
	kfree(logptr->path);
	logptr->path = NULL;
L
Linus Torvalds 已提交
395 396 397
	if (logptr->autorecording) {
		ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
		if (ret)
398
			pr_warn("vmlogrdr: failed to stop recording automatically\n");
L
Linus Torvalds 已提交
399 400 401 402 403 404 405
	}
	logptr->dev_in_use = 0;

	return 0;
}


406 407
static int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv)
{
L
Linus Torvalds 已提交
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
	int rc, *temp;
	/* we need to keep track of two data sizes here:
	 * The number of bytes we need to receive from iucv and
	 * the total number of bytes we actually write into the buffer.
	 */
	int user_data_count, iucv_data_count;
	char * buffer;

	if (atomic_read(&priv->receive_ready)) {
		spin_lock_bh(&priv->priv_lock);
		if (priv->residual_length){
			/* receive second half of a record */
			iucv_data_count = priv->residual_length;
			user_data_count = 0;
			buffer = priv->buffer;
		} else {
			/* receive a new record:
			 * We need to return the total length of the record
                         * + size of FENCE in the first 4 bytes of the buffer.
		         */
428
			iucv_data_count = priv->local_interrupt_buffer.length;
L
Linus Torvalds 已提交
429 430 431 432 433 434
			user_data_count = sizeof(int);
			temp = (int*)priv->buffer;
			*temp= iucv_data_count + sizeof(FENCE);
			buffer = priv->buffer + sizeof(int);
		}
		/*
435
		 * If the record is bigger than our buffer, we receive only
L
Linus Torvalds 已提交
436 437 438 439
		 * a part of it. We can get the rest later.
		 */
		if (iucv_data_count > NET_BUFFER_SIZE)
			iucv_data_count = NET_BUFFER_SIZE;
440 441 442 443
		rc = iucv_message_receive(priv->path,
					  &priv->local_interrupt_buffer,
					  0, buffer, iucv_data_count,
					  &priv->residual_length);
L
Linus Torvalds 已提交
444
		spin_unlock_bh(&priv->priv_lock);
445
		/* An rc of 5 indicates that the record was bigger than
L
Linus Torvalds 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
		 * the buffer, which is OK for us. A 9 indicates that the
		 * record was purged befor we could receive it.
		 */
		if (rc == 5)
			rc = 0;
		if (rc == 9)
			atomic_set(&priv->receive_ready, 0);
	} else {
		rc = 1;
	}
	if (!rc) {
		priv->buffer_free = 0;
 		user_data_count += iucv_data_count;
		priv->current_position = priv->buffer;
		if (priv->residual_length == 0){
			/* the whole record has been captured,
			 * now add the fence */
			atomic_dec(&priv->receive_ready);
			buffer = priv->buffer + user_data_count;
			memcpy(buffer, FENCE, sizeof(FENCE));
			user_data_count += sizeof(FENCE);
		}
		priv->remaining = user_data_count;
	}

	return rc;
}


475 476
static ssize_t vmlogrdr_read(struct file *filp, char __user *data,
			     size_t count, loff_t * ppos)
L
Linus Torvalds 已提交
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
{
	int rc;
	struct vmlogrdr_priv_t * priv = filp->private_data;

	while (priv->buffer_free) {
		rc = vmlogrdr_receive_data(priv);
		if (rc) {
			rc = wait_event_interruptible(read_wait_queue,
					atomic_read(&priv->receive_ready));
			if (rc)
				return rc;
		}
	}
	/* copy only up to end of record */
	if (count > priv->remaining)
		count = priv->remaining;

	if (copy_to_user(data, priv->current_position, count))
		return -EFAULT;

	*ppos += count;
	priv->current_position += count;
	priv->remaining -= count;

	/* if all data has been transferred, set buffer free */
	if (priv->remaining == 0)
		priv->buffer_free = 1;

	return count;
}

508 509 510 511
static ssize_t vmlogrdr_autopurge_store(struct device * dev,
					struct device_attribute *attr,
					const char * buf, size_t count)
{
512
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
	ssize_t ret = count;

	switch (buf[0]) {
	case '0':
		priv->autopurge=0;
		break;
	case '1':
		priv->autopurge=1;
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}


529 530 531 532
static ssize_t vmlogrdr_autopurge_show(struct device *dev,
				       struct device_attribute *attr,
				       char *buf)
{
533
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
534 535 536 537 538 539 540 541
	return sprintf(buf, "%u\n", priv->autopurge);
}


static DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
		   vmlogrdr_autopurge_store);


542 543 544 545
static ssize_t vmlogrdr_purge_store(struct device * dev,
				    struct device_attribute *attr,
				    const char * buf, size_t count)
{
L
Linus Torvalds 已提交
546 547 548

	char cp_command[80];
	char cp_response[80];
549
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572

	if (buf[0] != '1')
		return -EINVAL;

	memset(cp_command, 0x00, sizeof(cp_command));
	memset(cp_response, 0x00, sizeof(cp_response));

        /*
	 * The recording command needs to be called with option QID
	 * for guests that have previlege classes A or B.
	 * Other guests will not recognize the command and we have to
	 * issue the same command without the QID parameter.
	 */

	if (recording_class_AB)
		snprintf(cp_command, sizeof(cp_command),
			 "RECORDING %s PURGE QID * ",
			 priv->recording_name);
	else
		snprintf(cp_command, sizeof(cp_command),
			 "RECORDING %s PURGE ",
			 priv->recording_name);

573
	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
L
Linus Torvalds 已提交
574 575 576 577 578 579 580 581

	return count;
}


static DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);


582 583 584 585
static ssize_t vmlogrdr_autorecording_store(struct device *dev,
					    struct device_attribute *attr,
					    const char *buf, size_t count)
{
586
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	ssize_t ret = count;

	switch (buf[0]) {
	case '0':
		priv->autorecording=0;
		break;
	case '1':
		priv->autorecording=1;
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}


603 604 605 606
static ssize_t vmlogrdr_autorecording_show(struct device *dev,
					   struct device_attribute *attr,
					   char *buf)
{
607
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
608 609 610 611 612 613 614 615
	return sprintf(buf, "%u\n", priv->autorecording);
}


static DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
		   vmlogrdr_autorecording_store);


616 617 618 619
static ssize_t vmlogrdr_recording_store(struct device * dev,
					struct device_attribute *attr,
					const char * buf, size_t count)
{
620
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
	ssize_t ret;

	switch (buf[0]) {
	case '0':
		ret = vmlogrdr_recording(priv,0,0);
		break;
	case '1':
		ret = vmlogrdr_recording(priv,1,0);
		break;
	default:
		ret = -EINVAL;
	}
	if (ret)
		return ret;
	else
		return count;

}


static DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);


644 645 646
static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,
					      char *buf)
{
L
Linus Torvalds 已提交
647

648
	static const char cp_command[] = "QUERY RECORDING ";
L
Linus Torvalds 已提交
649 650
	int len;

651
	cpcmd(cp_command, buf, 4096, NULL);
L
Linus Torvalds 已提交
652 653 654 655 656
	len = strlen(buf);
	return len;
}
static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
		   NULL);
657 658 659 660 661 662 663 664 665 666 667
static struct attribute *vmlogrdr_drv_attrs[] = {
	&driver_attr_recording_status.attr,
	NULL,
};
static struct attribute_group vmlogrdr_drv_attr_group = {
	.attrs = vmlogrdr_drv_attrs,
};
static const struct attribute_group *vmlogrdr_drv_attr_groups[] = {
	&vmlogrdr_drv_attr_group,
	NULL,
};
L
Linus Torvalds 已提交
668 669 670 671 672 673 674 675

static struct attribute *vmlogrdr_attrs[] = {
	&dev_attr_autopurge.attr,
	&dev_attr_purge.attr,
	&dev_attr_autorecording.attr,
	&dev_attr_recording.attr,
	NULL,
};
676 677 678 679 680 681 682
static struct attribute_group vmlogrdr_attr_group = {
	.attrs = vmlogrdr_attrs,
};
static const struct attribute_group *vmlogrdr_attr_groups[] = {
	&vmlogrdr_attr_group,
	NULL,
};
L
Linus Torvalds 已提交
683

684 685 686
static int vmlogrdr_pm_prepare(struct device *dev)
{
	int rc;
M
Martin Schwidefsky 已提交
687
	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702

	rc = 0;
	if (priv) {
		spin_lock_bh(&priv->priv_lock);
		if (priv->dev_in_use)
			rc = -EBUSY;
		spin_unlock_bh(&priv->priv_lock);
	}
	if (rc)
		pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n",
		       dev_name(dev));
	return rc;
}


703
static const struct dev_pm_ops vmlogrdr_pm_ops = {
704 705 706
	.prepare = vmlogrdr_pm_prepare,
};

707
static struct class *vmlogrdr_class;
L
Linus Torvalds 已提交
708 709 710
static struct device_driver vmlogrdr_driver = {
	.name = "vmlogrdr",
	.bus  = &iucv_bus,
711
	.pm = &vmlogrdr_pm_ops,
712
	.groups = vmlogrdr_drv_attr_groups,
L
Linus Torvalds 已提交
713 714
};

715 716
static int vmlogrdr_register_driver(void)
{
L
Linus Torvalds 已提交
717 718
	int ret;

719 720
	/* Register with iucv driver */
	ret = iucv_register(&vmlogrdr_iucv_handler, 1);
721
	if (ret)
722 723
		goto out;

L
Linus Torvalds 已提交
724
	ret = driver_register(&vmlogrdr_driver);
725
	if (ret)
726
		goto out_iucv;
L
Linus Torvalds 已提交
727

728
	vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
L
Linus Torvalds 已提交
729
	if (IS_ERR(vmlogrdr_class)) {
730 731
		ret = PTR_ERR(vmlogrdr_class);
		vmlogrdr_class = NULL;
732
		goto out_driver;
L
Linus Torvalds 已提交
733 734 735
	}
	return 0;

736
out_driver:
L
Linus Torvalds 已提交
737
	driver_unregister(&vmlogrdr_driver);
738 739 740
out_iucv:
	iucv_unregister(&vmlogrdr_iucv_handler, 1);
out:
L
Linus Torvalds 已提交
741 742 743 744
	return ret;
}


745 746
static void vmlogrdr_unregister_driver(void)
{
747
	class_destroy(vmlogrdr_class);
L
Linus Torvalds 已提交
748 749
	vmlogrdr_class = NULL;
	driver_unregister(&vmlogrdr_driver);
750
	iucv_unregister(&vmlogrdr_iucv_handler, 1);
L
Linus Torvalds 已提交
751 752 753
}


754 755
static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
{
L
Linus Torvalds 已提交
756 757 758
	struct device *dev;
	int ret;

759
	dev = kzalloc(sizeof(struct device), GFP_KERNEL);
L
Linus Torvalds 已提交
760
	if (dev) {
761
		dev_set_name(dev, "%s", priv->internal_name);
L
Linus Torvalds 已提交
762 763 764
		dev->bus = &iucv_bus;
		dev->parent = iucv_root;
		dev->driver = &vmlogrdr_driver;
765
		dev->groups = vmlogrdr_attr_groups;
M
Martin Schwidefsky 已提交
766
		dev_set_drvdata(dev, priv);
L
Linus Torvalds 已提交
767 768 769 770 771 772 773 774 775 776 777
		/*
		 * The release function could be called after the
		 * module has been unloaded. It's _only_ task is to
		 * free the struct. Therefore, we specify kfree()
		 * directly here. (Probably a little bit obfuscating
		 * but legitime ...).
		 */
		dev->release = (void (*)(struct device *))kfree;
	} else
		return -ENOMEM;
	ret = device_register(dev);
778 779
	if (ret) {
		put_device(dev);
L
Linus Torvalds 已提交
780
		return ret;
781
	}
L
Linus Torvalds 已提交
782

783 784 785 786
	priv->class_device = device_create(vmlogrdr_class, dev,
					   MKDEV(vmlogrdr_major,
						 priv->minor_num),
					   priv, "%s", dev_name(dev));
L
Linus Torvalds 已提交
787 788 789 790 791 792 793 794 795 796 797
	if (IS_ERR(priv->class_device)) {
		ret = PTR_ERR(priv->class_device);
		priv->class_device=NULL;
		device_unregister(dev);
		return ret;
	}
	priv->device = dev;
	return 0;
}


798 799
static int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv)
{
800
	device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));
L
Linus Torvalds 已提交
801 802 803 804 805 806 807 808
	if (priv->device != NULL) {
		device_unregister(priv->device);
		priv->device=NULL;
	}
	return 0;
}


809 810
static int vmlogrdr_register_cdev(dev_t dev)
{
L
Linus Torvalds 已提交
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
	int rc = 0;
	vmlogrdr_cdev = cdev_alloc();
	if (!vmlogrdr_cdev) {
		return -ENOMEM;
	}
	vmlogrdr_cdev->owner = THIS_MODULE;
	vmlogrdr_cdev->ops = &vmlogrdr_fops;
	vmlogrdr_cdev->dev = dev;
	rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
	if (!rc)
		return 0;

	// cleanup: cdev is not fully registered, no cdev_del here!
	kobject_put(&vmlogrdr_cdev->kobj);
	vmlogrdr_cdev=NULL;
	return rc;
}


830 831
static void vmlogrdr_cleanup(void)
{
L
Linus Torvalds 已提交
832
        int i;
833

L
Linus Torvalds 已提交
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
	if (vmlogrdr_cdev) {
		cdev_del(vmlogrdr_cdev);
		vmlogrdr_cdev=NULL;
	}
	for (i=0; i < MAXMINOR; ++i ) {
		vmlogrdr_unregister_device(&sys_ser[i]);
		free_page((unsigned long)sys_ser[i].buffer);
	}
	vmlogrdr_unregister_driver();
	if (vmlogrdr_major) {
		unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
		vmlogrdr_major=0;
	}
}


850
static int __init vmlogrdr_init(void)
L
Linus Torvalds 已提交
851 852 853 854 855 856
{
	int rc;
	int i;
	dev_t dev;

	if (! MACHINE_IS_VM) {
857
		pr_err("not running under VM, driver not loaded.\n");
L
Linus Torvalds 已提交
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
		return -ENODEV;
	}

        recording_class_AB = vmlogrdr_get_recording_class_AB();

	rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
	if (rc)
		return rc;
	vmlogrdr_major = MAJOR(dev);

	rc=vmlogrdr_register_driver();
	if (rc)
		goto cleanup;

	for (i=0; i < MAXMINOR; ++i ) {
873
		sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
L
Linus Torvalds 已提交
874
		if (!sys_ser[i].buffer) {
875
			rc = -ENOMEM;
L
Linus Torvalds 已提交
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
			break;
		}
		sys_ser[i].current_position = sys_ser[i].buffer;
		rc=vmlogrdr_register_device(&sys_ser[i]);
		if (rc)
			break;
	}
	if (rc)
		goto cleanup;

	rc = vmlogrdr_register_cdev(dev);
	if (rc)
		goto cleanup;
	return 0;

cleanup:
	vmlogrdr_cleanup();
	return rc;
}


897
static void __exit vmlogrdr_exit(void)
L
Linus Torvalds 已提交
898 899 900 901 902 903 904 905
{
	vmlogrdr_cleanup();
	return;
}


module_init(vmlogrdr_init);
module_exit(vmlogrdr_exit);