18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Character device driver for extended error reporting. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2005 68c2ecf20Sopenharmony_ci * extended error reporting for DASD ECKD devices 78c2ecf20Sopenharmony_ci * Author(s): Stefan Weinhuber <wein@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "dasd-eckd" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/fs.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/poll.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/err.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci#include <linux/atomic.h> 268c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "dasd_int.h" 298c2ecf20Sopenharmony_ci#include "dasd_eckd.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#ifdef PRINTK_HEADER 328c2ecf20Sopenharmony_ci#undef PRINTK_HEADER 338c2ecf20Sopenharmony_ci#endif /* PRINTK_HEADER */ 348c2ecf20Sopenharmony_ci#define PRINTK_HEADER "dasd(eer):" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * SECTION: the internal buffer 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * The internal buffer is meant to store obaque blobs of data, so it does 428c2ecf20Sopenharmony_ci * not know of higher level concepts like triggers. 438c2ecf20Sopenharmony_ci * It consists of a number of pages that are used as a ringbuffer. Each data 448c2ecf20Sopenharmony_ci * blob is stored in a simple record that consists of an integer, which 458c2ecf20Sopenharmony_ci * contains the size of the following data, and the data bytes themselfes. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * To allow for multiple independent readers we create one internal buffer 488c2ecf20Sopenharmony_ci * each time the device is opened and destroy the buffer when the file is 498c2ecf20Sopenharmony_ci * closed again. The number of pages used for this buffer is determined by 508c2ecf20Sopenharmony_ci * the module parmeter eer_pages. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * One record can be written to a buffer by using the functions 538c2ecf20Sopenharmony_ci * - dasd_eer_start_record (one time per record to write the size to the 548c2ecf20Sopenharmony_ci * buffer and reserve the space for the data) 558c2ecf20Sopenharmony_ci * - dasd_eer_write_buffer (one or more times per record to write the data) 568c2ecf20Sopenharmony_ci * The data can be written in several steps but you will have to compute 578c2ecf20Sopenharmony_ci * the total size up front for the invocation of dasd_eer_start_record. 588c2ecf20Sopenharmony_ci * If the ringbuffer is full, dasd_eer_start_record will remove the required 598c2ecf20Sopenharmony_ci * number of old records. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * A record is typically read in two steps, first read the integer that 628c2ecf20Sopenharmony_ci * specifies the size of the following data, then read the data. 638c2ecf20Sopenharmony_ci * Both can be done by 648c2ecf20Sopenharmony_ci * - dasd_eer_read_buffer 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * For all mentioned functions you need to get the bufferlock first and keep 678c2ecf20Sopenharmony_ci * it until a complete record is written or read. 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * All information necessary to keep track of an internal buffer is kept in 708c2ecf20Sopenharmony_ci * a struct eerbuffer. The buffer specific to a file pointer is strored in 718c2ecf20Sopenharmony_ci * the private_data field of that file. To be able to write data to all 728c2ecf20Sopenharmony_ci * existing buffers, each buffer is also added to the bufferlist. 738c2ecf20Sopenharmony_ci * If the user does not want to read a complete record in one go, we have to 748c2ecf20Sopenharmony_ci * keep track of the rest of the record. residual stores the number of bytes 758c2ecf20Sopenharmony_ci * that are still to deliver. If the rest of the record is invalidated between 768c2ecf20Sopenharmony_ci * two reads then residual will be set to -1 so that the next read will fail. 778c2ecf20Sopenharmony_ci * All entries in the eerbuffer structure are protected with the bufferlock. 788c2ecf20Sopenharmony_ci * To avoid races between writing to a buffer on the one side and creating 798c2ecf20Sopenharmony_ci * and destroying buffers on the other side, the bufferlock must also be used 808c2ecf20Sopenharmony_ci * to protect the bufferlist. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int eer_pages = 5; 848c2ecf20Sopenharmony_cimodule_param(eer_pages, int, S_IRUGO|S_IWUSR); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct eerbuffer { 878c2ecf20Sopenharmony_ci struct list_head list; 888c2ecf20Sopenharmony_ci char **buffer; 898c2ecf20Sopenharmony_ci int buffersize; 908c2ecf20Sopenharmony_ci int buffer_page_count; 918c2ecf20Sopenharmony_ci int head; 928c2ecf20Sopenharmony_ci int tail; 938c2ecf20Sopenharmony_ci int residual; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic LIST_HEAD(bufferlist); 978c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(bufferlock); 988c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(dasd_eer_read_wait_queue); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * How many free bytes are available on the buffer. 1028c2ecf20Sopenharmony_ci * Needs to be called with bufferlock held. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic int dasd_eer_get_free_bytes(struct eerbuffer *eerb) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci if (eerb->head < eerb->tail) 1078c2ecf20Sopenharmony_ci return eerb->tail - eerb->head - 1; 1088c2ecf20Sopenharmony_ci return eerb->buffersize - eerb->head + eerb->tail -1; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * How many bytes of buffer space are used. 1138c2ecf20Sopenharmony_ci * Needs to be called with bufferlock held. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic int dasd_eer_get_filled_bytes(struct eerbuffer *eerb) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (eerb->head >= eerb->tail) 1198c2ecf20Sopenharmony_ci return eerb->head - eerb->tail; 1208c2ecf20Sopenharmony_ci return eerb->buffersize - eerb->tail + eerb->head; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * The dasd_eer_write_buffer function just copies count bytes of data 1258c2ecf20Sopenharmony_ci * to the buffer. Make sure to call dasd_eer_start_record first, to 1268c2ecf20Sopenharmony_ci * make sure that enough free space is available. 1278c2ecf20Sopenharmony_ci * Needs to be called with bufferlock held. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cistatic void dasd_eer_write_buffer(struct eerbuffer *eerb, 1308c2ecf20Sopenharmony_ci char *data, int count) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci unsigned long headindex,localhead; 1348c2ecf20Sopenharmony_ci unsigned long rest, len; 1358c2ecf20Sopenharmony_ci char *nextdata; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci nextdata = data; 1388c2ecf20Sopenharmony_ci rest = count; 1398c2ecf20Sopenharmony_ci while (rest > 0) { 1408c2ecf20Sopenharmony_ci headindex = eerb->head / PAGE_SIZE; 1418c2ecf20Sopenharmony_ci localhead = eerb->head % PAGE_SIZE; 1428c2ecf20Sopenharmony_ci len = min(rest, PAGE_SIZE - localhead); 1438c2ecf20Sopenharmony_ci memcpy(eerb->buffer[headindex]+localhead, nextdata, len); 1448c2ecf20Sopenharmony_ci nextdata += len; 1458c2ecf20Sopenharmony_ci rest -= len; 1468c2ecf20Sopenharmony_ci eerb->head += len; 1478c2ecf20Sopenharmony_ci if (eerb->head == eerb->buffersize) 1488c2ecf20Sopenharmony_ci eerb->head = 0; /* wrap around */ 1498c2ecf20Sopenharmony_ci BUG_ON(eerb->head > eerb->buffersize); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * Needs to be called with bufferlock held. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic int dasd_eer_read_buffer(struct eerbuffer *eerb, char *data, int count) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci unsigned long tailindex,localtail; 1608c2ecf20Sopenharmony_ci unsigned long rest, len, finalcount; 1618c2ecf20Sopenharmony_ci char *nextdata; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci finalcount = min(count, dasd_eer_get_filled_bytes(eerb)); 1648c2ecf20Sopenharmony_ci nextdata = data; 1658c2ecf20Sopenharmony_ci rest = finalcount; 1668c2ecf20Sopenharmony_ci while (rest > 0) { 1678c2ecf20Sopenharmony_ci tailindex = eerb->tail / PAGE_SIZE; 1688c2ecf20Sopenharmony_ci localtail = eerb->tail % PAGE_SIZE; 1698c2ecf20Sopenharmony_ci len = min(rest, PAGE_SIZE - localtail); 1708c2ecf20Sopenharmony_ci memcpy(nextdata, eerb->buffer[tailindex] + localtail, len); 1718c2ecf20Sopenharmony_ci nextdata += len; 1728c2ecf20Sopenharmony_ci rest -= len; 1738c2ecf20Sopenharmony_ci eerb->tail += len; 1748c2ecf20Sopenharmony_ci if (eerb->tail == eerb->buffersize) 1758c2ecf20Sopenharmony_ci eerb->tail = 0; /* wrap around */ 1768c2ecf20Sopenharmony_ci BUG_ON(eerb->tail > eerb->buffersize); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci return finalcount; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Whenever you want to write a blob of data to the internal buffer you 1838c2ecf20Sopenharmony_ci * have to start by using this function first. It will write the number 1848c2ecf20Sopenharmony_ci * of bytes that will be written to the buffer. If necessary it will remove 1858c2ecf20Sopenharmony_ci * old records to make room for the new one. 1868c2ecf20Sopenharmony_ci * Needs to be called with bufferlock held. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic int dasd_eer_start_record(struct eerbuffer *eerb, int count) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int tailcount; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (count + sizeof(count) > eerb->buffersize) 1938c2ecf20Sopenharmony_ci return -ENOMEM; 1948c2ecf20Sopenharmony_ci while (dasd_eer_get_free_bytes(eerb) < count + sizeof(count)) { 1958c2ecf20Sopenharmony_ci if (eerb->residual > 0) { 1968c2ecf20Sopenharmony_ci eerb->tail += eerb->residual; 1978c2ecf20Sopenharmony_ci if (eerb->tail >= eerb->buffersize) 1988c2ecf20Sopenharmony_ci eerb->tail -= eerb->buffersize; 1998c2ecf20Sopenharmony_ci eerb->residual = -1; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci dasd_eer_read_buffer(eerb, (char *) &tailcount, 2028c2ecf20Sopenharmony_ci sizeof(tailcount)); 2038c2ecf20Sopenharmony_ci eerb->tail += tailcount; 2048c2ecf20Sopenharmony_ci if (eerb->tail >= eerb->buffersize) 2058c2ecf20Sopenharmony_ci eerb->tail -= eerb->buffersize; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, (char*) &count, sizeof(count)); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * Release pages that are not used anymore. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic void dasd_eer_free_buffer_pages(char **buf, int no_pages) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (i = 0; i < no_pages; i++) 2208c2ecf20Sopenharmony_ci free_page((unsigned long) buf[i]); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* 2248c2ecf20Sopenharmony_ci * Allocate a new set of memory pages. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic int dasd_eer_allocate_buffer_pages(char **buf, int no_pages) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int i; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci for (i = 0; i < no_pages; i++) { 2318c2ecf20Sopenharmony_ci buf[i] = (char *) get_zeroed_page(GFP_KERNEL); 2328c2ecf20Sopenharmony_ci if (!buf[i]) { 2338c2ecf20Sopenharmony_ci dasd_eer_free_buffer_pages(buf, i); 2348c2ecf20Sopenharmony_ci return -ENOMEM; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* 2418c2ecf20Sopenharmony_ci * SECTION: The extended error reporting functionality 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * When a DASD device driver wants to report an error, it calls the 2468c2ecf20Sopenharmony_ci * function dasd_eer_write and gives the respective trigger ID as 2478c2ecf20Sopenharmony_ci * parameter. Currently there are four kinds of triggers: 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * DASD_EER_FATALERROR: all kinds of unrecoverable I/O problems 2508c2ecf20Sopenharmony_ci * DASD_EER_PPRCSUSPEND: PPRC was suspended 2518c2ecf20Sopenharmony_ci * DASD_EER_NOPATH: There is no path to the device left. 2528c2ecf20Sopenharmony_ci * DASD_EER_STATECHANGE: The state of the device has changed. 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * For the first three triggers all required information can be supplied by 2558c2ecf20Sopenharmony_ci * the caller. For these triggers a record is written by the function 2568c2ecf20Sopenharmony_ci * dasd_eer_write_standard_trigger. 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * The DASD_EER_STATECHANGE trigger is special since a sense subsystem 2598c2ecf20Sopenharmony_ci * status ccw need to be executed to gather the necessary sense data first. 2608c2ecf20Sopenharmony_ci * The dasd_eer_snss function will queue the SNSS request and the request 2618c2ecf20Sopenharmony_ci * callback will then call dasd_eer_write with the DASD_EER_STATCHANGE 2628c2ecf20Sopenharmony_ci * trigger. 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * To avoid memory allocations at runtime, the necessary memory is allocated 2658c2ecf20Sopenharmony_ci * when the extended error reporting is enabled for a device (by 2668c2ecf20Sopenharmony_ci * dasd_eer_probe). There is one sense subsystem status request for each 2678c2ecf20Sopenharmony_ci * eer enabled DASD device. The presence of the cqr in device->eer_cqr 2688c2ecf20Sopenharmony_ci * indicates that eer is enable for the device. The use of the snss request 2698c2ecf20Sopenharmony_ci * is protected by the DASD_FLAG_EER_IN_USE bit. When this flag indicates 2708c2ecf20Sopenharmony_ci * that the cqr is currently in use, dasd_eer_snss cannot start a second 2718c2ecf20Sopenharmony_ci * request but sets the DASD_FLAG_EER_SNSS flag instead. The callback of 2728c2ecf20Sopenharmony_ci * the SNSS request will check the bit and call dasd_eer_snss again. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci#define SNSS_DATA_SIZE 44 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci#define DASD_EER_BUSID_SIZE 10 2788c2ecf20Sopenharmony_cistruct dasd_eer_header { 2798c2ecf20Sopenharmony_ci __u32 total_size; 2808c2ecf20Sopenharmony_ci __u32 trigger; 2818c2ecf20Sopenharmony_ci __u64 tv_sec; 2828c2ecf20Sopenharmony_ci __u64 tv_usec; 2838c2ecf20Sopenharmony_ci char busid[DASD_EER_BUSID_SIZE]; 2848c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* 2878c2ecf20Sopenharmony_ci * The following function can be used for those triggers that have 2888c2ecf20Sopenharmony_ci * all necessary data available when the function is called. 2898c2ecf20Sopenharmony_ci * If the parameter cqr is not NULL, the chain of requests will be searched 2908c2ecf20Sopenharmony_ci * for valid sense data, and all valid sense data sets will be added to 2918c2ecf20Sopenharmony_ci * the triggers data. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic void dasd_eer_write_standard_trigger(struct dasd_device *device, 2948c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, 2958c2ecf20Sopenharmony_ci int trigger) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct dasd_ccw_req *temp_cqr; 2988c2ecf20Sopenharmony_ci int data_size; 2998c2ecf20Sopenharmony_ci struct timespec64 ts; 3008c2ecf20Sopenharmony_ci struct dasd_eer_header header; 3018c2ecf20Sopenharmony_ci unsigned long flags; 3028c2ecf20Sopenharmony_ci struct eerbuffer *eerb; 3038c2ecf20Sopenharmony_ci char *sense; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* go through cqr chain and count the valid sense data sets */ 3068c2ecf20Sopenharmony_ci data_size = 0; 3078c2ecf20Sopenharmony_ci for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) 3088c2ecf20Sopenharmony_ci if (dasd_get_sense(&temp_cqr->irb)) 3098c2ecf20Sopenharmony_ci data_size += 32; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci header.total_size = sizeof(header) + data_size + 4; /* "EOR" */ 3128c2ecf20Sopenharmony_ci header.trigger = trigger; 3138c2ecf20Sopenharmony_ci ktime_get_real_ts64(&ts); 3148c2ecf20Sopenharmony_ci header.tv_sec = ts.tv_sec; 3158c2ecf20Sopenharmony_ci header.tv_usec = ts.tv_nsec / NSEC_PER_USEC; 3168c2ecf20Sopenharmony_ci strlcpy(header.busid, dev_name(&device->cdev->dev), 3178c2ecf20Sopenharmony_ci DASD_EER_BUSID_SIZE); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 3208c2ecf20Sopenharmony_ci list_for_each_entry(eerb, &bufferlist, list) { 3218c2ecf20Sopenharmony_ci dasd_eer_start_record(eerb, header.total_size); 3228c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, (char *) &header, sizeof(header)); 3238c2ecf20Sopenharmony_ci for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) { 3248c2ecf20Sopenharmony_ci sense = dasd_get_sense(&temp_cqr->irb); 3258c2ecf20Sopenharmony_ci if (sense) 3268c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, sense, 32); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, "EOR", 4); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 3318c2ecf20Sopenharmony_ci wake_up_interruptible(&dasd_eer_read_wait_queue); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* 3358c2ecf20Sopenharmony_ci * This function writes a DASD_EER_STATECHANGE trigger. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_cistatic void dasd_eer_write_snss_trigger(struct dasd_device *device, 3388c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr, 3398c2ecf20Sopenharmony_ci int trigger) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int data_size; 3428c2ecf20Sopenharmony_ci int snss_rc; 3438c2ecf20Sopenharmony_ci struct timespec64 ts; 3448c2ecf20Sopenharmony_ci struct dasd_eer_header header; 3458c2ecf20Sopenharmony_ci unsigned long flags; 3468c2ecf20Sopenharmony_ci struct eerbuffer *eerb; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; 3498c2ecf20Sopenharmony_ci if (snss_rc) 3508c2ecf20Sopenharmony_ci data_size = 0; 3518c2ecf20Sopenharmony_ci else 3528c2ecf20Sopenharmony_ci data_size = SNSS_DATA_SIZE; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci header.total_size = sizeof(header) + data_size + 4; /* "EOR" */ 3558c2ecf20Sopenharmony_ci header.trigger = DASD_EER_STATECHANGE; 3568c2ecf20Sopenharmony_ci ktime_get_real_ts64(&ts); 3578c2ecf20Sopenharmony_ci header.tv_sec = ts.tv_sec; 3588c2ecf20Sopenharmony_ci header.tv_usec = ts.tv_nsec / NSEC_PER_USEC; 3598c2ecf20Sopenharmony_ci strlcpy(header.busid, dev_name(&device->cdev->dev), 3608c2ecf20Sopenharmony_ci DASD_EER_BUSID_SIZE); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 3638c2ecf20Sopenharmony_ci list_for_each_entry(eerb, &bufferlist, list) { 3648c2ecf20Sopenharmony_ci dasd_eer_start_record(eerb, header.total_size); 3658c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, (char *) &header , sizeof(header)); 3668c2ecf20Sopenharmony_ci if (!snss_rc) 3678c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, cqr->data, SNSS_DATA_SIZE); 3688c2ecf20Sopenharmony_ci dasd_eer_write_buffer(eerb, "EOR", 4); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 3718c2ecf20Sopenharmony_ci wake_up_interruptible(&dasd_eer_read_wait_queue); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* 3758c2ecf20Sopenharmony_ci * This function is called for all triggers. It calls the appropriate 3768c2ecf20Sopenharmony_ci * function that writes the actual trigger records. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_civoid dasd_eer_write(struct dasd_device *device, struct dasd_ccw_req *cqr, 3798c2ecf20Sopenharmony_ci unsigned int id) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci if (!device->eer_cqr) 3828c2ecf20Sopenharmony_ci return; 3838c2ecf20Sopenharmony_ci switch (id) { 3848c2ecf20Sopenharmony_ci case DASD_EER_FATALERROR: 3858c2ecf20Sopenharmony_ci case DASD_EER_PPRCSUSPEND: 3868c2ecf20Sopenharmony_ci dasd_eer_write_standard_trigger(device, cqr, id); 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci case DASD_EER_NOPATH: 3898c2ecf20Sopenharmony_ci case DASD_EER_NOSPC: 3908c2ecf20Sopenharmony_ci dasd_eer_write_standard_trigger(device, NULL, id); 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case DASD_EER_STATECHANGE: 3938c2ecf20Sopenharmony_ci dasd_eer_write_snss_trigger(device, cqr, id); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci default: /* unknown trigger, so we write it without any sense data */ 3968c2ecf20Sopenharmony_ci dasd_eer_write_standard_trigger(device, NULL, id); 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dasd_eer_write); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* 4038c2ecf20Sopenharmony_ci * Start a sense subsystem status request. 4048c2ecf20Sopenharmony_ci * Needs to be called with the device held. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_civoid dasd_eer_snss(struct dasd_device *device) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci cqr = device->eer_cqr; 4118c2ecf20Sopenharmony_ci if (!cqr) /* Device not eer enabled. */ 4128c2ecf20Sopenharmony_ci return; 4138c2ecf20Sopenharmony_ci if (test_and_set_bit(DASD_FLAG_EER_IN_USE, &device->flags)) { 4148c2ecf20Sopenharmony_ci /* Sense subsystem status request in use. */ 4158c2ecf20Sopenharmony_ci set_bit(DASD_FLAG_EER_SNSS, &device->flags); 4168c2ecf20Sopenharmony_ci return; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci /* cdev is already locked, can't use dasd_add_request_head */ 4198c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_EER_SNSS, &device->flags); 4208c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_QUEUED; 4218c2ecf20Sopenharmony_ci list_add(&cqr->devlist, &device->ccw_queue); 4228c2ecf20Sopenharmony_ci dasd_schedule_device_bh(device); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* 4268c2ecf20Sopenharmony_ci * Callback function for use with sense subsystem status request. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_cistatic void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct dasd_device *device = cqr->startdev; 4318c2ecf20Sopenharmony_ci unsigned long flags; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci dasd_eer_write(device, cqr, DASD_EER_STATECHANGE); 4348c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 4358c2ecf20Sopenharmony_ci if (device->eer_cqr == cqr) { 4368c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_EER_IN_USE, &device->flags); 4378c2ecf20Sopenharmony_ci if (test_bit(DASD_FLAG_EER_SNSS, &device->flags)) 4388c2ecf20Sopenharmony_ci /* Another SNSS has been requested in the meantime. */ 4398c2ecf20Sopenharmony_ci dasd_eer_snss(device); 4408c2ecf20Sopenharmony_ci cqr = NULL; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 4438c2ecf20Sopenharmony_ci if (cqr) 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Extended error recovery has been switched off while 4468c2ecf20Sopenharmony_ci * the SNSS request was running. It could even have 4478c2ecf20Sopenharmony_ci * been switched off and on again in which case there 4488c2ecf20Sopenharmony_ci * is a new ccw in device->eer_cqr. Free the "old" 4498c2ecf20Sopenharmony_ci * snss request now. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, device); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* 4558c2ecf20Sopenharmony_ci * Enable error reporting on a given device. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ciint dasd_eer_enable(struct dasd_device *device) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr = NULL; 4608c2ecf20Sopenharmony_ci unsigned long flags; 4618c2ecf20Sopenharmony_ci struct ccw1 *ccw; 4628c2ecf20Sopenharmony_ci int rc = 0; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 4658c2ecf20Sopenharmony_ci if (device->eer_cqr) 4668c2ecf20Sopenharmony_ci goto out; 4678c2ecf20Sopenharmony_ci else if (!device->discipline || 4688c2ecf20Sopenharmony_ci strcmp(device->discipline->name, "ECKD")) 4698c2ecf20Sopenharmony_ci rc = -EMEDIUMTYPE; 4708c2ecf20Sopenharmony_ci else if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) 4718c2ecf20Sopenharmony_ci rc = -EBUSY; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (rc) 4748c2ecf20Sopenharmony_ci goto out; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */, 4778c2ecf20Sopenharmony_ci SNSS_DATA_SIZE, device, NULL); 4788c2ecf20Sopenharmony_ci if (IS_ERR(cqr)) { 4798c2ecf20Sopenharmony_ci rc = -ENOMEM; 4808c2ecf20Sopenharmony_ci cqr = NULL; 4818c2ecf20Sopenharmony_ci goto out; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci cqr->startdev = device; 4858c2ecf20Sopenharmony_ci cqr->retries = 255; 4868c2ecf20Sopenharmony_ci cqr->expires = 10 * HZ; 4878c2ecf20Sopenharmony_ci clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); 4888c2ecf20Sopenharmony_ci set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ccw = cqr->cpaddr; 4918c2ecf20Sopenharmony_ci ccw->cmd_code = DASD_ECKD_CCW_SNSS; 4928c2ecf20Sopenharmony_ci ccw->count = SNSS_DATA_SIZE; 4938c2ecf20Sopenharmony_ci ccw->flags = 0; 4948c2ecf20Sopenharmony_ci ccw->cda = (__u32)(addr_t) cqr->data; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci cqr->buildclk = get_tod_clock(); 4978c2ecf20Sopenharmony_ci cqr->status = DASD_CQR_FILLED; 4988c2ecf20Sopenharmony_ci cqr->callback = dasd_eer_snss_cb; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (!device->eer_cqr) { 5018c2ecf20Sopenharmony_ci device->eer_cqr = cqr; 5028c2ecf20Sopenharmony_ci cqr = NULL; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciout: 5068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (cqr) 5098c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, device); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return rc; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/* 5158c2ecf20Sopenharmony_ci * Disable error reporting on a given device. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_civoid dasd_eer_disable(struct dasd_device *device) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct dasd_ccw_req *cqr; 5208c2ecf20Sopenharmony_ci unsigned long flags; 5218c2ecf20Sopenharmony_ci int in_use; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (!device->eer_cqr) 5248c2ecf20Sopenharmony_ci return; 5258c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 5268c2ecf20Sopenharmony_ci cqr = device->eer_cqr; 5278c2ecf20Sopenharmony_ci device->eer_cqr = NULL; 5288c2ecf20Sopenharmony_ci clear_bit(DASD_FLAG_EER_SNSS, &device->flags); 5298c2ecf20Sopenharmony_ci in_use = test_and_clear_bit(DASD_FLAG_EER_IN_USE, &device->flags); 5308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 5318c2ecf20Sopenharmony_ci if (cqr && !in_use) 5328c2ecf20Sopenharmony_ci dasd_sfree_request(cqr, device); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/* 5368c2ecf20Sopenharmony_ci * SECTION: the device operations 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* 5408c2ecf20Sopenharmony_ci * On the one side we need a lock to access our internal buffer, on the 5418c2ecf20Sopenharmony_ci * other side a copy_to_user can sleep. So we need to copy the data we have 5428c2ecf20Sopenharmony_ci * to transfer in a readbuffer, which is protected by the readbuffer_mutex. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_cistatic char readbuffer[PAGE_SIZE]; 5458c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(readbuffer_mutex); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int dasd_eer_open(struct inode *inp, struct file *filp) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct eerbuffer *eerb; 5508c2ecf20Sopenharmony_ci unsigned long flags; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL); 5538c2ecf20Sopenharmony_ci if (!eerb) 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci eerb->buffer_page_count = eer_pages; 5568c2ecf20Sopenharmony_ci if (eerb->buffer_page_count < 1 || 5578c2ecf20Sopenharmony_ci eerb->buffer_page_count > INT_MAX / PAGE_SIZE) { 5588c2ecf20Sopenharmony_ci kfree(eerb); 5598c2ecf20Sopenharmony_ci DBF_EVENT(DBF_WARNING, "can't open device since module " 5608c2ecf20Sopenharmony_ci "parameter eer_pages is smaller than 1 or" 5618c2ecf20Sopenharmony_ci " bigger than %d", (int)(INT_MAX / PAGE_SIZE)); 5628c2ecf20Sopenharmony_ci return -EINVAL; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE; 5658c2ecf20Sopenharmony_ci eerb->buffer = kmalloc_array(eerb->buffer_page_count, sizeof(char *), 5668c2ecf20Sopenharmony_ci GFP_KERNEL); 5678c2ecf20Sopenharmony_ci if (!eerb->buffer) { 5688c2ecf20Sopenharmony_ci kfree(eerb); 5698c2ecf20Sopenharmony_ci return -ENOMEM; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci if (dasd_eer_allocate_buffer_pages(eerb->buffer, 5728c2ecf20Sopenharmony_ci eerb->buffer_page_count)) { 5738c2ecf20Sopenharmony_ci kfree(eerb->buffer); 5748c2ecf20Sopenharmony_ci kfree(eerb); 5758c2ecf20Sopenharmony_ci return -ENOMEM; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci filp->private_data = eerb; 5788c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 5798c2ecf20Sopenharmony_ci list_add(&eerb->list, &bufferlist); 5808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return nonseekable_open(inp,filp); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int dasd_eer_close(struct inode *inp, struct file *filp) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct eerbuffer *eerb; 5888c2ecf20Sopenharmony_ci unsigned long flags; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci eerb = (struct eerbuffer *) filp->private_data; 5918c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 5928c2ecf20Sopenharmony_ci list_del(&eerb->list); 5938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 5948c2ecf20Sopenharmony_ci dasd_eer_free_buffer_pages(eerb->buffer, eerb->buffer_page_count); 5958c2ecf20Sopenharmony_ci kfree(eerb->buffer); 5968c2ecf20Sopenharmony_ci kfree(eerb); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic ssize_t dasd_eer_read(struct file *filp, char __user *buf, 6028c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci int tc,rc; 6058c2ecf20Sopenharmony_ci int tailcount,effective_count; 6068c2ecf20Sopenharmony_ci unsigned long flags; 6078c2ecf20Sopenharmony_ci struct eerbuffer *eerb; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci eerb = (struct eerbuffer *) filp->private_data; 6108c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&readbuffer_mutex)) 6118c2ecf20Sopenharmony_ci return -ERESTARTSYS; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (eerb->residual < 0) { /* the remainder of this record */ 6168c2ecf20Sopenharmony_ci /* has been deleted */ 6178c2ecf20Sopenharmony_ci eerb->residual = 0; 6188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 6198c2ecf20Sopenharmony_ci mutex_unlock(&readbuffer_mutex); 6208c2ecf20Sopenharmony_ci return -EIO; 6218c2ecf20Sopenharmony_ci } else if (eerb->residual > 0) { 6228c2ecf20Sopenharmony_ci /* OK we still have a second half of a record to deliver */ 6238c2ecf20Sopenharmony_ci effective_count = min(eerb->residual, (int) count); 6248c2ecf20Sopenharmony_ci eerb->residual -= effective_count; 6258c2ecf20Sopenharmony_ci } else { 6268c2ecf20Sopenharmony_ci tc = 0; 6278c2ecf20Sopenharmony_ci while (!tc) { 6288c2ecf20Sopenharmony_ci tc = dasd_eer_read_buffer(eerb, (char *) &tailcount, 6298c2ecf20Sopenharmony_ci sizeof(tailcount)); 6308c2ecf20Sopenharmony_ci if (!tc) { 6318c2ecf20Sopenharmony_ci /* no data available */ 6328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 6338c2ecf20Sopenharmony_ci mutex_unlock(&readbuffer_mutex); 6348c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) 6358c2ecf20Sopenharmony_ci return -EAGAIN; 6368c2ecf20Sopenharmony_ci rc = wait_event_interruptible( 6378c2ecf20Sopenharmony_ci dasd_eer_read_wait_queue, 6388c2ecf20Sopenharmony_ci eerb->head != eerb->tail); 6398c2ecf20Sopenharmony_ci if (rc) 6408c2ecf20Sopenharmony_ci return rc; 6418c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&readbuffer_mutex)) 6428c2ecf20Sopenharmony_ci return -ERESTARTSYS; 6438c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci WARN_ON(tc != sizeof(tailcount)); 6478c2ecf20Sopenharmony_ci effective_count = min(tailcount,(int)count); 6488c2ecf20Sopenharmony_ci eerb->residual = tailcount - effective_count; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci tc = dasd_eer_read_buffer(eerb, readbuffer, effective_count); 6528c2ecf20Sopenharmony_ci WARN_ON(tc != effective_count); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (copy_to_user(buf, readbuffer, effective_count)) { 6578c2ecf20Sopenharmony_ci mutex_unlock(&readbuffer_mutex); 6588c2ecf20Sopenharmony_ci return -EFAULT; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci mutex_unlock(&readbuffer_mutex); 6628c2ecf20Sopenharmony_ci return effective_count; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic __poll_t dasd_eer_poll(struct file *filp, poll_table *ptable) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci __poll_t mask; 6688c2ecf20Sopenharmony_ci unsigned long flags; 6698c2ecf20Sopenharmony_ci struct eerbuffer *eerb; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci eerb = (struct eerbuffer *) filp->private_data; 6728c2ecf20Sopenharmony_ci poll_wait(filp, &dasd_eer_read_wait_queue, ptable); 6738c2ecf20Sopenharmony_ci spin_lock_irqsave(&bufferlock, flags); 6748c2ecf20Sopenharmony_ci if (eerb->head != eerb->tail) 6758c2ecf20Sopenharmony_ci mask = EPOLLIN | EPOLLRDNORM ; 6768c2ecf20Sopenharmony_ci else 6778c2ecf20Sopenharmony_ci mask = 0; 6788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bufferlock, flags); 6798c2ecf20Sopenharmony_ci return mask; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic const struct file_operations dasd_eer_fops = { 6838c2ecf20Sopenharmony_ci .open = &dasd_eer_open, 6848c2ecf20Sopenharmony_ci .release = &dasd_eer_close, 6858c2ecf20Sopenharmony_ci .read = &dasd_eer_read, 6868c2ecf20Sopenharmony_ci .poll = &dasd_eer_poll, 6878c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6888c2ecf20Sopenharmony_ci .llseek = noop_llseek, 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic struct miscdevice *dasd_eer_dev = NULL; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ciint __init dasd_eer_init(void) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci int rc; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci dasd_eer_dev = kzalloc(sizeof(*dasd_eer_dev), GFP_KERNEL); 6988c2ecf20Sopenharmony_ci if (!dasd_eer_dev) 6998c2ecf20Sopenharmony_ci return -ENOMEM; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci dasd_eer_dev->minor = MISC_DYNAMIC_MINOR; 7028c2ecf20Sopenharmony_ci dasd_eer_dev->name = "dasd_eer"; 7038c2ecf20Sopenharmony_ci dasd_eer_dev->fops = &dasd_eer_fops; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci rc = misc_register(dasd_eer_dev); 7068c2ecf20Sopenharmony_ci if (rc) { 7078c2ecf20Sopenharmony_ci kfree(dasd_eer_dev); 7088c2ecf20Sopenharmony_ci dasd_eer_dev = NULL; 7098c2ecf20Sopenharmony_ci DBF_EVENT(DBF_ERR, "%s", "dasd_eer_init could not " 7108c2ecf20Sopenharmony_ci "register misc device"); 7118c2ecf20Sopenharmony_ci return rc; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci return 0; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_civoid dasd_eer_exit(void) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci if (dasd_eer_dev) { 7208c2ecf20Sopenharmony_ci misc_deregister(dasd_eer_dev); 7218c2ecf20Sopenharmony_ci kfree(dasd_eer_dev); 7228c2ecf20Sopenharmony_ci dasd_eer_dev = NULL; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci} 725