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 + -
显示快捷键?