dasd_eer.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 683 行 · 第 1/2 页

C
683
字号
/* *  Character device driver for extended error reporting. * *  Copyright (C) 2005 IBM Corporation *  extended error reporting for DASD ECKD devices *  Author(s): Stefan Weinhuber <wein@de.ibm.com> */#include <linux/init.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/miscdevice.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/device.h>#include <linux/poll.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#include <asm/atomic.h>#include <asm/ebcdic.h>#include "dasd_int.h"#include "dasd_eckd.h"#ifdef PRINTK_HEADER#undef PRINTK_HEADER#endif				/* PRINTK_HEADER */#define PRINTK_HEADER "dasd(eer):"/* * SECTION: the internal buffer *//* * The internal buffer is meant to store obaque blobs of data, so it does * not know of higher level concepts like triggers. * It consists of a number of pages that are used as a ringbuffer. Each data * blob is stored in a simple record that consists of an integer, which * contains the size of the following data, and the data bytes themselfes. * * To allow for multiple independent readers we create one internal buffer * each time the device is opened and destroy the buffer when the file is * closed again. The number of pages used for this buffer is determined by * the module parmeter eer_pages. * * One record can be written to a buffer by using the functions * - dasd_eer_start_record (one time per record to write the size to the *                          buffer and reserve the space for the data) * - dasd_eer_write_buffer (one or more times per record to write the data) * The data can be written in several steps but you will have to compute * the total size up front for the invocation of dasd_eer_start_record. * If the ringbuffer is full, dasd_eer_start_record will remove the required * number of old records. * * A record is typically read in two steps, first read the integer that * specifies the size of the following data, then read the data. * Both can be done by * - dasd_eer_read_buffer * * For all mentioned functions you need to get the bufferlock first and keep * it until a complete record is written or read. * * All information necessary to keep track of an internal buffer is kept in * a struct eerbuffer. The buffer specific to a file pointer is strored in * the private_data field of that file. To be able to write data to all * existing buffers, each buffer is also added to the bufferlist. * If the user does not want to read a complete record in one go, we have to * keep track of the rest of the record. residual stores the number of bytes * that are still to deliver. If the rest of the record is invalidated between * two reads then residual will be set to -1 so that the next read will fail. * All entries in the eerbuffer structure are protected with the bufferlock. * To avoid races between writing to a buffer on the one side and creating * and destroying buffers on the other side, the bufferlock must also be used * to protect the bufferlist. */static int eer_pages = 5;module_param(eer_pages, int, S_IRUGO|S_IWUSR);struct eerbuffer {	struct list_head list;	char **buffer;	int buffersize;	int buffer_page_count;	int head;        int tail;	int residual;};static LIST_HEAD(bufferlist);static spinlock_t bufferlock = SPIN_LOCK_UNLOCKED;static DECLARE_WAIT_QUEUE_HEAD(dasd_eer_read_wait_queue);/* * How many free bytes are available on the buffer. * Needs to be called with bufferlock held. */static int dasd_eer_get_free_bytes(struct eerbuffer *eerb){	if (eerb->head < eerb->tail)		return eerb->tail - eerb->head - 1;	return eerb->buffersize - eerb->head + eerb->tail -1;}/* * How many bytes of buffer space are used. * Needs to be called with bufferlock held. */static int dasd_eer_get_filled_bytes(struct eerbuffer *eerb){	if (eerb->head >= eerb->tail)		return eerb->head - eerb->tail;	return eerb->buffersize - eerb->tail + eerb->head;}/* * The dasd_eer_write_buffer function just copies count bytes of data * to the buffer. Make sure to call dasd_eer_start_record first, to * make sure that enough free space is available. * Needs to be called with bufferlock held. */static void dasd_eer_write_buffer(struct eerbuffer *eerb,				  char *data, int count){	unsigned long headindex,localhead;	unsigned long rest, len;	char *nextdata;	nextdata = data;	rest = count;	while (rest > 0) { 		headindex = eerb->head / PAGE_SIZE; 		localhead = eerb->head % PAGE_SIZE;		len = min(rest, PAGE_SIZE - localhead);		memcpy(eerb->buffer[headindex]+localhead, nextdata, len);		nextdata += len;		rest -= len;		eerb->head += len;		if (eerb->head == eerb->buffersize)			eerb->head = 0; /* wrap around */		BUG_ON(eerb->head > eerb->buffersize);	}}/* * Needs to be called with bufferlock held. */static int dasd_eer_read_buffer(struct eerbuffer *eerb, char *data, int count){	unsigned long tailindex,localtail;	unsigned long rest, len, finalcount;	char *nextdata;	finalcount = min(count, dasd_eer_get_filled_bytes(eerb));	nextdata = data;	rest = finalcount;	while (rest > 0) { 		tailindex = eerb->tail / PAGE_SIZE; 		localtail = eerb->tail % PAGE_SIZE;		len = min(rest, PAGE_SIZE - localtail);		memcpy(nextdata, eerb->buffer[tailindex] + localtail, len);		nextdata += len;		rest -= len;		eerb->tail += len;		if (eerb->tail == eerb->buffersize)			eerb->tail = 0; /* wrap around */		BUG_ON(eerb->tail > eerb->buffersize);	}	return finalcount;}/* * Whenever you want to write a blob of data to the internal buffer you * have to start by using this function first. It will write the number * of bytes that will be written to the buffer. If necessary it will remove * old records to make room for the new one. * Needs to be called with bufferlock held. */static int dasd_eer_start_record(struct eerbuffer *eerb, int count){	int tailcount;	if (count + sizeof(count) > eerb->buffersize)		return -ENOMEM;	while (dasd_eer_get_free_bytes(eerb) < count + sizeof(count)) {		if (eerb->residual > 0) {			eerb->tail += eerb->residual;			if (eerb->tail >= eerb->buffersize)				eerb->tail -= eerb->buffersize;			eerb->residual = -1;		}		dasd_eer_read_buffer(eerb, (char *) &tailcount,				     sizeof(tailcount));		eerb->tail += tailcount;		if (eerb->tail >= eerb->buffersize)			eerb->tail -= eerb->buffersize;	}	dasd_eer_write_buffer(eerb, (char*) &count, sizeof(count));	return 0;};/* * Release pages that are not used anymore. */static void dasd_eer_free_buffer_pages(char **buf, int no_pages){	int i;	for (i = 0; i < no_pages; i++)		free_page((unsigned long) buf[i]);}/* * Allocate a new set of memory pages. */static int dasd_eer_allocate_buffer_pages(char **buf, int no_pages){	int i;	for (i = 0; i < no_pages; i++) {		buf[i] = (char *) get_zeroed_page(GFP_KERNEL);		if (!buf[i]) {			dasd_eer_free_buffer_pages(buf, i);			return -ENOMEM;		}	}	return 0;}/* * SECTION: The extended error reporting functionality *//* * When a DASD device driver wants to report an error, it calls the * function dasd_eer_write and gives the respective trigger ID as * parameter. Currently there are four kinds of triggers: * * DASD_EER_FATALERROR:  all kinds of unrecoverable I/O problems * DASD_EER_PPRCSUSPEND: PPRC was suspended * DASD_EER_NOPATH:      There is no path to the device left. * DASD_EER_STATECHANGE: The state of the device has changed. * * For the first three triggers all required information can be supplied by * the caller. For these triggers a record is written by the function * dasd_eer_write_standard_trigger. * * The DASD_EER_STATECHANGE trigger is special since a sense subsystem * status ccw need to be executed to gather the necessary sense data first. * The dasd_eer_snss function will queue the SNSS request and the request * callback will then call dasd_eer_write with the DASD_EER_STATCHANGE * trigger. * * To avoid memory allocations at runtime, the necessary memory is allocated * when the extended error reporting is enabled for a device (by * dasd_eer_probe). There is one sense subsystem status request for each * eer enabled DASD device. The presence of the cqr in device->eer_cqr * indicates that eer is enable for the device. The use of the snss request * is protected by the DASD_FLAG_EER_IN_USE bit. When this flag indicates * that the cqr is currently in use, dasd_eer_snss cannot start a second * request but sets the DASD_FLAG_EER_SNSS flag instead. The callback of * the SNSS request will check the bit and call dasd_eer_snss again. */#define SNSS_DATA_SIZE 44#define DASD_EER_BUSID_SIZE 10struct dasd_eer_header {	__u32 total_size;	__u32 trigger;	__u64 tv_sec;	__u64 tv_usec;	char busid[DASD_EER_BUSID_SIZE];};/* * The following function can be used for those triggers that have * all necessary data available when the function is called. * If the parameter cqr is not NULL, the chain of requests will be searched * for valid sense data, and all valid sense data sets will be added to * the triggers data. */static void dasd_eer_write_standard_trigger(struct dasd_device *device,					    struct dasd_ccw_req *cqr,					    int trigger){	struct dasd_ccw_req *temp_cqr;	int data_size;	struct timeval tv;	struct dasd_eer_header header;	unsigned long flags;	struct eerbuffer *eerb;	/* go through cqr chain and count the valid sense data sets */	data_size = 0;	for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers)		if (temp_cqr->irb.esw.esw0.erw.cons)			data_size += 32;	header.total_size = sizeof(header) + data_size + 4; /* "EOR" */	header.trigger = trigger;	do_gettimeofday(&tv);	header.tv_sec = tv.tv_sec;	header.tv_usec = tv.tv_usec;	strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);	spin_lock_irqsave(&bufferlock, flags);	list_for_each_entry(eerb, &bufferlist, list) {		dasd_eer_start_record(eerb, header.total_size);		dasd_eer_write_buffer(eerb, (char *) &header, sizeof(header));		for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers)			if (temp_cqr->irb.esw.esw0.erw.cons)				dasd_eer_write_buffer(eerb, cqr->irb.ecw, 32);		dasd_eer_write_buffer(eerb, "EOR", 4);	}	spin_unlock_irqrestore(&bufferlock, flags);	wake_up_interruptible(&dasd_eer_read_wait_queue);}/* * This function writes a DASD_EER_STATECHANGE trigger. */static void dasd_eer_write_snss_trigger(struct dasd_device *device,					struct dasd_ccw_req *cqr,					int trigger){	int data_size;	int snss_rc;	struct timeval tv;	struct dasd_eer_header header;	unsigned long flags;	struct eerbuffer *eerb;	snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;	if (snss_rc)		data_size = 0;	else

⌨️ 快捷键说明

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