⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vmlogrdr.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * drivers/s390/char/vmlogrdr.c *	character device driver for reading z/VM system service records * * *	Copyright (C) 2004 IBM Corporation *	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> * */#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <asm/atomic.h>#include <asm/uaccess.h>#include <asm/cpcmd.h>#include <asm/debug.h>#include <asm/ebcdic.h>#include "../net/iucv.h"#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];	u16 pathid;	int connection_established;	int iucv_path_severed;	iucv_MessagePending local_interrupt_buffer;	atomic_t receive_ready;	iucv_handle_t iucv_handle;	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;	struct class_device  *class_device;	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 *);static ssize_t vmlogrdr_read (struct file *filp, char *data, size_t count,			       loff_t * ppos);static struct file_operations vmlogrdr_fops = {	.owner   = THIS_MODULE,	.open    = vmlogrdr_open,	.release = vmlogrdr_release,	.read    = vmlogrdr_read,};static u8 iucvMagic[16] = {	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};static u8 mask[] = {	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};static u8 iucv_host[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };static voidvmlogrdr_iucv_ConnectionComplete(iucv_ConnectionComplete *eib, void *pgm_data);static voidvmlogrdr_iucv_ConnectionSevered(iucv_ConnectionSevered *eib, void *pgm_data);static voidvmlogrdr_iucv_MessagePending(iucv_MessagePending *eib, void *pgm_data);static iucv_interrupt_ops_t vmlogrdr_iucvops = {	.ConnectionComplete = vmlogrdr_iucv_ConnectionComplete,	.ConnectionSevered  = vmlogrdr_iucv_ConnectionSevered,	.MessagePending     = vmlogrdr_iucv_MessagePending,};DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);/* * 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,	  .priv_lock      = SPIN_LOCK_UNLOCKED,	  .autorecording  = 1,	  .autopurge      = 1,	},	{ .system_service = "*ACCOUNT",	  .internal_name  = "account",	  .recording_name = "ACCOUNT",	  .minor_num      = 1,	  .buffer_free    = 1,	  .priv_lock      = SPIN_LOCK_UNLOCKED,	  .autorecording  = 1,	  .autopurge      = 1,	},	{ .system_service = "*SYMPTOM",	  .internal_name  = "symptom",	  .recording_name = "SYMPTOM",	  .minor_num      = 2,	  .buffer_free    = 1,	  .priv_lock      = SPIN_LOCK_UNLOCKED,	  .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;static voidvmlogrdr_iucv_ConnectionComplete (iucv_ConnectionComplete * eib,				   void * pgm_data){	struct vmlogrdr_priv_t * logptr = pgm_data;	spin_lock(&logptr->priv_lock);	logptr->connection_established = 1;	spin_unlock(&logptr->priv_lock);	wake_up(&conn_wait_queue);	return;}static voidvmlogrdr_iucv_ConnectionSevered (iucv_ConnectionSevered * eib, void * pgm_data){	u8 reason = (u8) eib->ipuser[8];	struct vmlogrdr_priv_t * logptr = pgm_data;	printk (KERN_ERR "vmlogrdr: connection severed with"		" reason %i\n", reason);	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);}static voidvmlogrdr_iucv_MessagePending (iucv_MessagePending * eib, void * pgm_data){	struct vmlogrdr_priv_t * logptr = pgm_data;	/*	 * 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);	memcpy(&(logptr->local_interrupt_buffer), eib, sizeof(*eib));	atomic_inc(&logptr->receive_ready);	spin_unlock(&logptr->priv_lock);	wake_up_interruptible(&read_wait_queue);}static intvmlogrdr_get_recording_class_AB(void) {	char cp_command[]="QUERY COMMAND RECORDING ";	char cp_response[80];	char *tail;	int len,i;	printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command);	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);	printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response);	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;}static intvmlogrdr_recording(struct vmlogrdr_priv_t * logptr, int action, int purge) {	char cp_command[80];	char cp_response[160];	char *onoff, *qid_string;	memset(cp_command, 0x00, sizeof(cp_command));	memset(cp_response, 0x00, sizeof(cp_response));        onoff = ((action == 1) ? "ON" : "OFF");	qid_string = ((recording_class_AB == 1) ? " QID * " : "");        /*	 * 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.	 */	if (purge) {		snprintf(cp_command, sizeof(cp_command),			 "RECORDING %s PURGE %s",			 logptr->recording_name,			 qid_string);		printk (KERN_DEBUG "vmlogrdr: recording command: %s\n",			cp_command);		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);		printk (KERN_DEBUG "vmlogrdr: recording response: %s",			cp_response);	}	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);	printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);	printk (KERN_DEBUG "vmlogrdr: recording response: %s",		cp_response);	/* 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         * 'Command complete'. So I use strstr rather then the strncmp.	 */	if (strstr(cp_response,"Command complete"))		return 0;	else		return -EIO;}static intvmlogrdr_open (struct inode *inode, struct file *filp){	int dev_num = 0;	struct vmlogrdr_priv_t * logptr = NULL;	int connect_rc = 0;	int ret;	dev_num = iminor(inode);	if (dev_num > MAXMINOR)		return -ENODEV;	logptr = &sys_ser[dev_num];	if (logptr == NULL)		return -ENODEV;	/*	 * only allow for blocking reads to be open	 */	if (filp->f_flags & O_NONBLOCK)		return -ENOSYS;	/* 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;	} else {		logptr->dev_in_use = 1;		spin_unlock_bh(&logptr->priv_lock);	}	atomic_set(&logptr->receive_ready, 0);	logptr->buffer_free = 1;	/* set the file options */	filp->private_data = logptr;	filp->f_op = &vmlogrdr_fops;	/* start recording for this service*/	ret=0;	if (logptr->autorecording)		ret = vmlogrdr_recording(logptr,1,logptr->autopurge);	if (ret)		printk (KERN_WARNING "vmlogrdr: failed to start "			"recording automatically\n");	/* Register with iucv driver */	logptr->iucv_handle = iucv_register_program(iucvMagic,			logptr->system_service, mask, &vmlogrdr_iucvops,			logptr);	if (logptr->iucv_handle == NULL) {		printk (KERN_ERR "vmlogrdr: failed to register with"			"iucv driver\n");		goto not_registered;	}	/* create connection to the system service */	spin_lock_bh(&logptr->priv_lock);	logptr->connection_established = 0;	logptr->iucv_path_severed = 0;	spin_unlock_bh(&logptr->priv_lock);	connect_rc = iucv_connect (&(logptr->pathid), 10, iucvMagic,					logptr->system_service, iucv_host, 0,					NULL, NULL,					logptr->iucv_handle, NULL);	if (connect_rc) {		printk (KERN_ERR "vmlogrdr: iucv connection to %s "			"failed with rc %i \n", logptr->system_service,			connect_rc);		goto not_connected;	}	/* 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));	if (logptr->iucv_path_severed) {		goto not_connected;	} 	return nonseekable_open(inode, filp);not_connected:	iucv_unregister_program(logptr->iucv_handle);	logptr->iucv_handle = NULL;not_registered:	if (logptr->autorecording)		vmlogrdr_recording(logptr,0,logptr->autopurge);	logptr->dev_in_use = 0;	return -EIO;}static intvmlogrdr_release (struct inode *inode, struct file *filp){	int ret;	struct vmlogrdr_priv_t * logptr = filp->private_data;	iucv_unregister_program(logptr->iucv_handle);	logptr->iucv_handle = NULL;	if (logptr->autorecording) {		ret = vmlogrdr_recording(logptr,0,logptr->autopurge);		if (ret)			printk (KERN_WARNING "vmlogrdr: failed to stop "				"recording automatically\n");	}	logptr->dev_in_use = 0;	return 0;}static intvmlogrdr_receive_data(struct vmlogrdr_priv_t *priv) {	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:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -