📄 vmlogrdr.c
字号:
/* * 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 + -