monreader.c 16.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * Character device driver for reading z/VM *MONITOR service records.
 *
4 5 6
 * Copyright IBM Corp. 2004, 2009
 *
 * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
L
Linus Torvalds 已提交
7 8
 */

9 10 11
#define KMSG_COMPONENT "monreader"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

L
Linus Torvalds 已提交
12 13 14 15 16 17 18 19 20 21
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
22
#include <linux/poll.h>
23
#include <linux/device.h>
24
#include <linux/slab.h>
25
#include <net/iucv/iucv.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include <asm/uaccess.h>
#include <asm/ebcdic.h>
#include <asm/extmem.h>


#define MON_COLLECT_SAMPLE 0x80
#define MON_COLLECT_EVENT  0x40
#define MON_SERVICE	   "*MONITOR"
#define MON_IN_USE	   0x01
#define MON_MSGLIM	   255

static char mon_dcss_name[9] = "MONDCSS\0";

struct mon_msg {
	u32 pos;
	u32 mca_offset;
42
	struct iucv_message msg;
L
Linus Torvalds 已提交
43 44 45 46 47
	char msglim_reached;
	char replied_msglim;
};

struct mon_private {
48
	struct iucv_path *path;
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
	struct mon_msg *msg_array[MON_MSGLIM];
	unsigned int   write_index;
	unsigned int   read_index;
	atomic_t msglim_count;
	atomic_t read_ready;
	atomic_t iucv_connected;
	atomic_t iucv_severed;
};

static unsigned long mon_in_use = 0;

static unsigned long mon_dcss_start;
static unsigned long mon_dcss_end;

static DECLARE_WAIT_QUEUE_HEAD(mon_read_wait_queue);
static DECLARE_WAIT_QUEUE_HEAD(mon_conn_wait_queue);

static u8 user_data_connect[16] = {
	/* Version code, must be 0x01 for shared mode */
	0x01,
	/* what to collect */
	MON_COLLECT_SAMPLE | MON_COLLECT_EVENT,
	/* DCSS name in EBCDIC, 8 bytes padded with blanks */
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};

static u8 user_data_sever[16] = {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};

81
static struct device *monreader_device;
L
Linus Torvalds 已提交
82 83 84 85 86 87 88 89

/******************************************************************************
 *                             helper functions                               *
 *****************************************************************************/
/*
 * Create the 8 bytes EBCDIC DCSS segment name from
 * an ASCII name, incl. padding
 */
90
static void dcss_mkname(char *ascii_name, char *ebcdic_name)
L
Linus Torvalds 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103
{
	int i;

	for (i = 0; i < 8; i++) {
		if (ascii_name[i] == '\0')
			break;
		ebcdic_name[i] = toupper(ascii_name[i]);
	};
	for (; i < 8; i++)
		ebcdic_name[i] = ' ';
	ASCEBC(ebcdic_name, 8);
}

104
static inline unsigned long mon_mca_start(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
105
{
106
	return *(u32 *) &monmsg->msg.rmmsg;
L
Linus Torvalds 已提交
107 108
}

109
static inline unsigned long mon_mca_end(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
110
{
111
	return *(u32 *) &monmsg->msg.rmmsg[4];
L
Linus Torvalds 已提交
112 113
}

114
static inline u8 mon_mca_type(struct mon_msg *monmsg, u8 index)
L
Linus Torvalds 已提交
115 116 117 118
{
	return *((u8 *) mon_mca_start(monmsg) + monmsg->mca_offset + index);
}

119
static inline u32 mon_mca_size(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
120 121 122 123
{
	return mon_mca_end(monmsg) - mon_mca_start(monmsg) + 1;
}

124
static inline u32 mon_rec_start(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
125 126 127 128
{
	return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 4));
}

129
static inline u32 mon_rec_end(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
130 131 132 133
{
	return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8));
}

134
static int mon_check_mca(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148
{
	if ((mon_rec_end(monmsg) <= mon_rec_start(monmsg)) ||
	    (mon_rec_start(monmsg) < mon_dcss_start) ||
	    (mon_rec_end(monmsg) > mon_dcss_end) ||
	    (mon_mca_type(monmsg, 0) == 0) ||
	    (mon_mca_size(monmsg) % 12 != 0) ||
	    (mon_mca_end(monmsg) <= mon_mca_start(monmsg)) ||
	    (mon_mca_end(monmsg) > mon_dcss_end) ||
	    (mon_mca_start(monmsg) < mon_dcss_start) ||
	    ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0)))
		return -EINVAL;
	return 0;
}

149 150
static int mon_send_reply(struct mon_msg *monmsg,
			  struct mon_private *monpriv)
L
Linus Torvalds 已提交
151 152 153
{
	int rc;

154 155
	rc = iucv_message_reply(monpriv->path, &monmsg->msg,
				IUCV_IPRMDATA, NULL, 0);
L
Linus Torvalds 已提交
156 157 158 159 160 161 162 163 164 165
	atomic_dec(&monpriv->msglim_count);
	if (likely(!monmsg->msglim_reached)) {
		monmsg->pos = 0;
		monmsg->mca_offset = 0;
		monpriv->read_index = (monpriv->read_index + 1) %
				      MON_MSGLIM;
		atomic_dec(&monpriv->read_ready);
	} else
		monmsg->replied_msglim = 1;
	if (rc) {
166
		pr_err("Reading monitor data failed with rc=%i\n", rc);
L
Linus Torvalds 已提交
167 168 169 170 171
		return -EIO;
	}
	return 0;
}

172
static void mon_free_mem(struct mon_private *monpriv)
173 174 175 176
{
	int i;

	for (i = 0; i < MON_MSGLIM; i++)
177
		kfree(monpriv->msg_array[i]);
178 179 180
	kfree(monpriv);
}

181
static struct mon_private *mon_alloc_mem(void)
L
Linus Torvalds 已提交
182
{
183
	int i;
L
Linus Torvalds 已提交
184 185
	struct mon_private *monpriv;

186
	monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
187
	if (!monpriv)
L
Linus Torvalds 已提交
188 189
		return NULL;
	for (i = 0; i < MON_MSGLIM; i++) {
190
		monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg),
L
Linus Torvalds 已提交
191 192
						    GFP_KERNEL);
		if (!monpriv->msg_array[i]) {
193
			mon_free_mem(monpriv);
L
Linus Torvalds 已提交
194 195 196 197 198 199
			return NULL;
		}
	}
	return monpriv;
}

200
static inline void mon_next_mca(struct mon_msg *monmsg)
L
Linus Torvalds 已提交
201 202 203 204 205 206 207
{
	if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12))
		return;
	monmsg->mca_offset += 12;
	monmsg->pos = 0;
}

208
static struct mon_msg *mon_next_message(struct mon_private *monpriv)
L
Linus Torvalds 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
{
	struct mon_msg *monmsg;

	if (!atomic_read(&monpriv->read_ready))
		return NULL;
	monmsg = monpriv->msg_array[monpriv->read_index];
	if (unlikely(monmsg->replied_msglim)) {
		monmsg->replied_msglim = 0;
		monmsg->msglim_reached = 0;
		monmsg->pos = 0;
		monmsg->mca_offset = 0;
		monpriv->read_index = (monpriv->read_index + 1) %
				      MON_MSGLIM;
		atomic_dec(&monpriv->read_ready);
		return ERR_PTR(-EOVERFLOW);
	}
	return monmsg;
}


/******************************************************************************
 *                               IUCV handler                                 *
 *****************************************************************************/
232
static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
L
Linus Torvalds 已提交
233
{
234
	struct mon_private *monpriv = path->private;
L
Linus Torvalds 已提交
235 236 237 238 239

	atomic_set(&monpriv->iucv_connected, 1);
	wake_up(&mon_conn_wait_queue);
}

240
static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
L
Linus Torvalds 已提交
241
{
242
	struct mon_private *monpriv = path->private;
L
Linus Torvalds 已提交
243

244 245
	pr_err("z/VM *MONITOR system service disconnected with rc=%i\n",
	       ipuser[0]);
246
	iucv_path_sever(path, NULL);
L
Linus Torvalds 已提交
247 248 249 250 251
	atomic_set(&monpriv->iucv_severed, 1);
	wake_up(&mon_conn_wait_queue);
	wake_up_interruptible(&mon_read_wait_queue);
}

252 253
static void mon_iucv_message_pending(struct iucv_path *path,
				     struct iucv_message *msg)
L
Linus Torvalds 已提交
254
{
255
	struct mon_private *monpriv = path->private;
L
Linus Torvalds 已提交
256

257 258
	memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
	       msg, sizeof(*msg));
L
Linus Torvalds 已提交
259
	if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
260
		pr_warning("The read queue for monitor data is full\n");
L
Linus Torvalds 已提交
261 262 263 264 265 266 267
		monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
	}
	monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
	atomic_inc(&monpriv->read_ready);
	wake_up_interruptible(&mon_read_wait_queue);
}

268 269 270 271
static struct iucv_handler monreader_iucv_handler = {
	.path_complete	 = mon_iucv_path_complete,
	.path_severed	 = mon_iucv_path_severed,
	.message_pending = mon_iucv_message_pending,
L
Linus Torvalds 已提交
272 273 274 275 276
};

/******************************************************************************
 *                               file operations                              *
 *****************************************************************************/
277
static int mon_open(struct inode *inode, struct file *filp)
L
Linus Torvalds 已提交
278 279
{
	struct mon_private *monpriv;
280
	int rc;
L
Linus Torvalds 已提交
281 282 283 284

	/*
	 * only one user allowed
	 */
285
	rc = -EBUSY;
L
Linus Torvalds 已提交
286
	if (test_and_set_bit(MON_IN_USE, &mon_in_use))
287
		goto out;
L
Linus Torvalds 已提交
288

289
	rc = -ENOMEM;
L
Linus Torvalds 已提交
290 291
	monpriv = mon_alloc_mem();
	if (!monpriv)
292
		goto out_use;
L
Linus Torvalds 已提交
293 294

	/*
295
	 * Connect to *MONITOR service
L
Linus Torvalds 已提交
296
	 */
297 298 299 300 301
	monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
	if (!monpriv->path)
		goto out_priv;
	rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
			       MON_SERVICE, NULL, user_data_connect, monpriv);
L
Linus Torvalds 已提交
302
	if (rc) {
303 304
		pr_err("Connecting to the z/VM *MONITOR system service "
		       "failed with rc=%i\n", rc);
L
Linus Torvalds 已提交
305
		rc = -EIO;
306
		goto out_path;
L
Linus Torvalds 已提交
307 308 309 310 311 312 313 314 315 316 317
	}
	/*
	 * Wait for connection confirmation
	 */
	wait_event(mon_conn_wait_queue,
		   atomic_read(&monpriv->iucv_connected) ||
		   atomic_read(&monpriv->iucv_severed));
	if (atomic_read(&monpriv->iucv_severed)) {
		atomic_set(&monpriv->iucv_severed, 0);
		atomic_set(&monpriv->iucv_connected, 0);
		rc = -EIO;
318
		goto out_path;
L
Linus Torvalds 已提交
319 320
	}
	filp->private_data = monpriv;
321
	dev_set_drvdata(monreader_device, monpriv);
L
Linus Torvalds 已提交
322 323
	return nonseekable_open(inode, filp);

324
out_path:
325
	iucv_path_free(monpriv->path);
326 327 328
out_priv:
	mon_free_mem(monpriv);
out_use:
L
Linus Torvalds 已提交
329
	clear_bit(MON_IN_USE, &mon_in_use);
330
out:
L
Linus Torvalds 已提交
331 332 333
	return rc;
}

334
static int mon_close(struct inode *inode, struct file *filp)
L
Linus Torvalds 已提交
335 336 337 338 339 340 341
{
	int rc, i;
	struct mon_private *monpriv = filp->private_data;

	/*
	 * Close IUCV connection and unregister
	 */
342 343 344 345 346 347 348
	if (monpriv->path) {
		rc = iucv_path_sever(monpriv->path, user_data_sever);
		if (rc)
			pr_warning("Disconnecting the z/VM *MONITOR system "
				   "service failed with rc=%i\n", rc);
		iucv_path_free(monpriv->path);
	}
L
Linus Torvalds 已提交
349 350 351 352 353 354 355

	atomic_set(&monpriv->iucv_severed, 0);
	atomic_set(&monpriv->iucv_connected, 0);
	atomic_set(&monpriv->read_ready, 0);
	atomic_set(&monpriv->msglim_count, 0);
	monpriv->write_index  = 0;
	monpriv->read_index   = 0;
356
	dev_set_drvdata(monreader_device, NULL);
L
Linus Torvalds 已提交
357 358 359 360 361 362 363 364

	for (i = 0; i < MON_MSGLIM; i++)
		kfree(monpriv->msg_array[i]);
	kfree(monpriv);
	clear_bit(MON_IN_USE, &mon_in_use);
	return 0;
}

365 366
static ssize_t mon_read(struct file *filp, char __user *data,
			size_t count, loff_t *ppos)
L
Linus Torvalds 已提交
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
{
	struct mon_private *monpriv = filp->private_data;
	struct mon_msg *monmsg;
	int ret;
	u32 mce_start;

	monmsg = mon_next_message(monpriv);
	if (IS_ERR(monmsg))
		return PTR_ERR(monmsg);

	if (!monmsg) {
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		ret = wait_event_interruptible(mon_read_wait_queue,
					atomic_read(&monpriv->read_ready) ||
					atomic_read(&monpriv->iucv_severed));
		if (ret)
			return ret;
		if (unlikely(atomic_read(&monpriv->iucv_severed)))
			return -EIO;
		monmsg = monpriv->msg_array[monpriv->read_index];
	}

390
	if (!monmsg->pos)
L
Linus Torvalds 已提交
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
		monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset;
	if (mon_check_mca(monmsg))
		goto reply;

	/* read monitor control element (12 bytes) first */
	mce_start = mon_mca_start(monmsg) + monmsg->mca_offset;
	if ((monmsg->pos >= mce_start) && (monmsg->pos < mce_start + 12)) {
		count = min(count, (size_t) mce_start + 12 - monmsg->pos);
		ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos,
				   count);
		if (ret)
			return -EFAULT;
		monmsg->pos += count;
		if (monmsg->pos == mce_start + 12)
			monmsg->pos = mon_rec_start(monmsg);
		goto out_copy;
	}

	/* read records */
	if (monmsg->pos <= mon_rec_end(monmsg)) {
		count = min(count, (size_t) mon_rec_end(monmsg) - monmsg->pos
					    + 1);
		ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos,
				   count);
		if (ret)
			return -EFAULT;
		monmsg->pos += count;
		if (monmsg->pos > mon_rec_end(monmsg))
			mon_next_mca(monmsg);
		goto out_copy;
	}
reply:
	ret = mon_send_reply(monmsg, monpriv);
	return ret;

out_copy:
	*ppos += count;
	return count;
}

431
static unsigned int mon_poll(struct file *filp, struct poll_table_struct *p)
L
Linus Torvalds 已提交
432 433 434 435 436 437 438 439 440 441 442
{
	struct mon_private *monpriv = filp->private_data;

	poll_wait(filp, &mon_read_wait_queue, p);
	if (unlikely(atomic_read(&monpriv->iucv_severed)))
		return POLLERR;
	if (atomic_read(&monpriv->read_ready))
		return POLLIN | POLLRDNORM;
	return 0;
}

443
static const struct file_operations mon_fops = {
L
Linus Torvalds 已提交
444 445 446 447 448
	.owner   = THIS_MODULE,
	.open    = &mon_open,
	.release = &mon_close,
	.read    = &mon_read,
	.poll    = &mon_poll,
449
	.llseek  = noop_llseek,
L
Linus Torvalds 已提交
450 451 452 453 454 455 456 457
};

static struct miscdevice mon_dev = {
	.name       = "monreader",
	.fops       = &mon_fops,
	.minor      = MISC_DYNAMIC_MINOR,
};

458 459 460 461 462 463

/******************************************************************************
 *				suspend / resume			      *
 *****************************************************************************/
static int monreader_freeze(struct device *dev)
{
464
	struct mon_private *monpriv = dev_get_drvdata(dev);
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
	int rc;

	if (!monpriv)
		return 0;
	if (monpriv->path) {
		rc = iucv_path_sever(monpriv->path, user_data_sever);
		if (rc)
			pr_warning("Disconnecting the z/VM *MONITOR system "
				   "service failed with rc=%i\n", rc);
		iucv_path_free(monpriv->path);
	}
	atomic_set(&monpriv->iucv_severed, 0);
	atomic_set(&monpriv->iucv_connected, 0);
	atomic_set(&monpriv->read_ready, 0);
	atomic_set(&monpriv->msglim_count, 0);
	monpriv->write_index  = 0;
	monpriv->read_index   = 0;
	monpriv->path = NULL;
	return 0;
}

static int monreader_thaw(struct device *dev)
{
M
Martin Schwidefsky 已提交
488
	struct mon_private *monpriv = dev_get_drvdata(dev);
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
	int rc;

	if (!monpriv)
		return 0;
	rc = -ENOMEM;
	monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
	if (!monpriv->path)
		goto out;
	rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
			       MON_SERVICE, NULL, user_data_connect, monpriv);
	if (rc) {
		pr_err("Connecting to the z/VM *MONITOR system service "
		       "failed with rc=%i\n", rc);
		goto out_path;
	}
	wait_event(mon_conn_wait_queue,
		   atomic_read(&monpriv->iucv_connected) ||
		   atomic_read(&monpriv->iucv_severed));
	if (atomic_read(&monpriv->iucv_severed))
		goto out_path;
	return 0;
out_path:
	rc = -EIO;
	iucv_path_free(monpriv->path);
	monpriv->path = NULL;
out:
	atomic_set(&monpriv->iucv_severed, 1);
	return rc;
}

static int monreader_restore(struct device *dev)
{
	int rc;

	segment_unload(mon_dcss_name);
	rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
			  &mon_dcss_start, &mon_dcss_end);
	if (rc < 0) {
		segment_warning(rc, mon_dcss_name);
		panic("fatal monreader resume error: no monitor dcss\n");
	}
	return monreader_thaw(dev);
}

533
static const struct dev_pm_ops monreader_pm_ops = {
534 535 536 537 538 539 540 541 542 543 544 545
	.freeze  = monreader_freeze,
	.thaw	 = monreader_thaw,
	.restore = monreader_restore,
};

static struct device_driver monreader_driver = {
	.name = "monreader",
	.bus  = &iucv_bus,
	.pm   = &monreader_pm_ops,
};


L
Linus Torvalds 已提交
546 547 548
/******************************************************************************
 *                              module init/exit                              *
 *****************************************************************************/
549
static int __init mon_init(void)
L
Linus Torvalds 已提交
550 551 552 553
{
	int rc;

	if (!MACHINE_IS_VM) {
554 555
		pr_err("The z/VM *MONITOR record device driver cannot be "
		       "loaded without z/VM\n");
L
Linus Torvalds 已提交
556 557 558
		return -ENODEV;
	}

559 560 561 562 563
	/*
	 * Register with IUCV and connect to *MONITOR service
	 */
	rc = iucv_register(&monreader_iucv_handler, 1);
	if (rc) {
564 565
		pr_err("The z/VM *MONITOR record device driver failed to "
		       "register with IUCV\n");
566 567 568
		return rc;
	}

569 570 571 572
	rc = driver_register(&monreader_driver);
	if (rc)
		goto out_iucv;
	monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
573 574
	if (!monreader_device) {
		rc = -ENOMEM;
575
		goto out_driver;
576 577
	}

578 579 580 581 582 583 584
	dev_set_name(monreader_device, "monreader-dev");
	monreader_device->bus = &iucv_bus;
	monreader_device->parent = iucv_root;
	monreader_device->driver = &monreader_driver;
	monreader_device->release = (void (*)(struct device *))kfree;
	rc = device_register(monreader_device);
	if (rc) {
585
		put_device(monreader_device);
586 587 588
		goto out_driver;
	}

L
Linus Torvalds 已提交
589 590
	rc = segment_type(mon_dcss_name);
	if (rc < 0) {
591
		segment_warning(rc, mon_dcss_name);
592
		goto out_device;
L
Linus Torvalds 已提交
593 594
	}
	if (rc != SEG_TYPE_SC) {
595 596
		pr_err("The specified *MONITOR DCSS %s does not have the "
		       "required type SC\n", mon_dcss_name);
597
		rc = -EINVAL;
598
		goto out_device;
L
Linus Torvalds 已提交
599 600 601 602 603
	}

	rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
			  &mon_dcss_start, &mon_dcss_end);
	if (rc < 0) {
604
		segment_warning(rc, mon_dcss_name);
605
		rc = -EINVAL;
606
		goto out_device;
L
Linus Torvalds 已提交
607 608 609
	}
	dcss_mkname(mon_dcss_name, &user_data_connect[8]);

610 611 612 613
	/*
	 * misc_register() has to be the last action in module_init(), because
	 * file operations will be available right after this.
	 */
L
Linus Torvalds 已提交
614
	rc = misc_register(&mon_dev);
615
	if (rc < 0 )
L
Linus Torvalds 已提交
616 617 618 619 620
		goto out;
	return 0;

out:
	segment_unload(mon_dcss_name);
621 622 623 624
out_device:
	device_unregister(monreader_device);
out_driver:
	driver_unregister(&monreader_driver);
625 626
out_iucv:
	iucv_unregister(&monreader_iucv_handler, 1);
L
Linus Torvalds 已提交
627 628 629
	return rc;
}

630
static void __exit mon_exit(void)
L
Linus Torvalds 已提交
631 632
{
	segment_unload(mon_dcss_name);
633
	misc_deregister(&mon_dev);
634 635
	device_unregister(monreader_device);
	driver_unregister(&monreader_driver);
636
	iucv_unregister(&monreader_iucv_handler, 1);
L
Linus Torvalds 已提交
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	return;
}


module_init(mon_init);
module_exit(mon_exit);

module_param_string(mondcss, mon_dcss_name, 9, 0444);
MODULE_PARM_DESC(mondcss, "Name of DCSS segment to be used for *MONITOR "
		 "service, max. 8 chars. Default is MONDCSS");

MODULE_AUTHOR("Gerald Schaefer <geraldsc@de.ibm.com>");
MODULE_DESCRIPTION("Character device driver for reading z/VM "
		   "monitor service records.");
MODULE_LICENSE("GPL");