brintos

brintos / linux-shallow public Read only

0
0
Text · 20.1 KiB · 194e9e2 Raw
719 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  Character device driver for extended error reporting.4 *5 *  Copyright IBM Corp. 20056 *  extended error reporting for DASD ECKD devices7 *  Author(s): Stefan Weinhuber <wein@de.ibm.com>8 */9 10#include <linux/init.h>11#include <linux/fs.h>12#include <linux/kernel.h>13#include <linux/miscdevice.h>14#include <linux/module.h>15#include <linux/moduleparam.h>16#include <linux/device.h>17#include <linux/poll.h>18#include <linux/mutex.h>19#include <linux/err.h>20#include <linux/slab.h>21 22#include <linux/uaccess.h>23#include <linux/atomic.h>24#include <asm/ebcdic.h>25 26#include "dasd_int.h"27#include "dasd_eckd.h"28 29/*30 * SECTION: the internal buffer31 */32 33/*34 * The internal buffer is meant to store obaque blobs of data, so it does35 * not know of higher level concepts like triggers.36 * It consists of a number of pages that are used as a ringbuffer. Each data37 * blob is stored in a simple record that consists of an integer, which38 * contains the size of the following data, and the data bytes themselfes.39 *40 * To allow for multiple independent readers we create one internal buffer41 * each time the device is opened and destroy the buffer when the file is42 * closed again. The number of pages used for this buffer is determined by43 * the module parmeter eer_pages.44 *45 * One record can be written to a buffer by using the functions46 * - dasd_eer_start_record (one time per record to write the size to the47 *                          buffer and reserve the space for the data)48 * - dasd_eer_write_buffer (one or more times per record to write the data)49 * The data can be written in several steps but you will have to compute50 * the total size up front for the invocation of dasd_eer_start_record.51 * If the ringbuffer is full, dasd_eer_start_record will remove the required52 * number of old records.53 *54 * A record is typically read in two steps, first read the integer that55 * specifies the size of the following data, then read the data.56 * Both can be done by57 * - dasd_eer_read_buffer58 *59 * For all mentioned functions you need to get the bufferlock first and keep60 * it until a complete record is written or read.61 *62 * All information necessary to keep track of an internal buffer is kept in63 * a struct eerbuffer. The buffer specific to a file pointer is strored in64 * the private_data field of that file. To be able to write data to all65 * existing buffers, each buffer is also added to the bufferlist.66 * If the user does not want to read a complete record in one go, we have to67 * keep track of the rest of the record. residual stores the number of bytes68 * that are still to deliver. If the rest of the record is invalidated between69 * two reads then residual will be set to -1 so that the next read will fail.70 * All entries in the eerbuffer structure are protected with the bufferlock.71 * To avoid races between writing to a buffer on the one side and creating72 * and destroying buffers on the other side, the bufferlock must also be used73 * to protect the bufferlist.74 */75 76static int eer_pages = 5;77module_param(eer_pages, int, S_IRUGO|S_IWUSR);78 79struct eerbuffer {80	struct list_head list;81	char **buffer;82	int buffersize;83	int buffer_page_count;84	int head;85        int tail;86	int residual;87};88 89static LIST_HEAD(bufferlist);90static DEFINE_SPINLOCK(bufferlock);91static DECLARE_WAIT_QUEUE_HEAD(dasd_eer_read_wait_queue);92 93/*94 * How many free bytes are available on the buffer.95 * Needs to be called with bufferlock held.96 */97static int dasd_eer_get_free_bytes(struct eerbuffer *eerb)98{99	if (eerb->head < eerb->tail)100		return eerb->tail - eerb->head - 1;101	return eerb->buffersize - eerb->head + eerb->tail -1;102}103 104/*105 * How many bytes of buffer space are used.106 * Needs to be called with bufferlock held.107 */108static int dasd_eer_get_filled_bytes(struct eerbuffer *eerb)109{110 111	if (eerb->head >= eerb->tail)112		return eerb->head - eerb->tail;113	return eerb->buffersize - eerb->tail + eerb->head;114}115 116/*117 * The dasd_eer_write_buffer function just copies count bytes of data118 * to the buffer. Make sure to call dasd_eer_start_record first, to119 * make sure that enough free space is available.120 * Needs to be called with bufferlock held.121 */122static void dasd_eer_write_buffer(struct eerbuffer *eerb,123				  char *data, int count)124{125 126	unsigned long headindex,localhead;127	unsigned long rest, len;128	char *nextdata;129 130	nextdata = data;131	rest = count;132	while (rest > 0) {133 		headindex = eerb->head / PAGE_SIZE;134 		localhead = eerb->head % PAGE_SIZE;135		len = min(rest, PAGE_SIZE - localhead);136		memcpy(eerb->buffer[headindex]+localhead, nextdata, len);137		nextdata += len;138		rest -= len;139		eerb->head += len;140		if (eerb->head == eerb->buffersize)141			eerb->head = 0; /* wrap around */142		BUG_ON(eerb->head > eerb->buffersize);143	}144}145 146/*147 * Needs to be called with bufferlock held.148 */149static int dasd_eer_read_buffer(struct eerbuffer *eerb, char *data, int count)150{151 152	unsigned long tailindex,localtail;153	unsigned long rest, len, finalcount;154	char *nextdata;155 156	finalcount = min(count, dasd_eer_get_filled_bytes(eerb));157	nextdata = data;158	rest = finalcount;159	while (rest > 0) {160 		tailindex = eerb->tail / PAGE_SIZE;161 		localtail = eerb->tail % PAGE_SIZE;162		len = min(rest, PAGE_SIZE - localtail);163		memcpy(nextdata, eerb->buffer[tailindex] + localtail, len);164		nextdata += len;165		rest -= len;166		eerb->tail += len;167		if (eerb->tail == eerb->buffersize)168			eerb->tail = 0; /* wrap around */169		BUG_ON(eerb->tail > eerb->buffersize);170	}171	return finalcount;172}173 174/*175 * Whenever you want to write a blob of data to the internal buffer you176 * have to start by using this function first. It will write the number177 * of bytes that will be written to the buffer. If necessary it will remove178 * old records to make room for the new one.179 * Needs to be called with bufferlock held.180 */181static int dasd_eer_start_record(struct eerbuffer *eerb, int count)182{183	int tailcount;184 185	if (count + sizeof(count) > eerb->buffersize)186		return -ENOMEM;187	while (dasd_eer_get_free_bytes(eerb) < count + sizeof(count)) {188		if (eerb->residual > 0) {189			eerb->tail += eerb->residual;190			if (eerb->tail >= eerb->buffersize)191				eerb->tail -= eerb->buffersize;192			eerb->residual = -1;193		}194		dasd_eer_read_buffer(eerb, (char *) &tailcount,195				     sizeof(tailcount));196		eerb->tail += tailcount;197		if (eerb->tail >= eerb->buffersize)198			eerb->tail -= eerb->buffersize;199	}200	dasd_eer_write_buffer(eerb, (char*) &count, sizeof(count));201 202	return 0;203};204 205/*206 * Release pages that are not used anymore.207 */208static void dasd_eer_free_buffer_pages(char **buf, int no_pages)209{210	int i;211 212	for (i = 0; i < no_pages; i++)213		free_page((unsigned long) buf[i]);214}215 216/*217 * Allocate a new set of memory pages.218 */219static int dasd_eer_allocate_buffer_pages(char **buf, int no_pages)220{221	int i;222 223	for (i = 0; i < no_pages; i++) {224		buf[i] = (char *) get_zeroed_page(GFP_KERNEL);225		if (!buf[i]) {226			dasd_eer_free_buffer_pages(buf, i);227			return -ENOMEM;228		}229	}230	return 0;231}232 233/*234 * SECTION: The extended error reporting functionality235 */236 237/*238 * When a DASD device driver wants to report an error, it calls the239 * function dasd_eer_write and gives the respective trigger ID as240 * parameter. Currently there are four kinds of triggers:241 *242 * DASD_EER_FATALERROR:  all kinds of unrecoverable I/O problems243 * DASD_EER_PPRCSUSPEND: PPRC was suspended244 * DASD_EER_NOPATH:      There is no path to the device left.245 * DASD_EER_STATECHANGE: The state of the device has changed.246 *247 * For the first three triggers all required information can be supplied by248 * the caller. For these triggers a record is written by the function249 * dasd_eer_write_standard_trigger.250 *251 * The DASD_EER_STATECHANGE trigger is special since a sense subsystem252 * status ccw need to be executed to gather the necessary sense data first.253 * The dasd_eer_snss function will queue the SNSS request and the request254 * callback will then call dasd_eer_write with the DASD_EER_STATCHANGE255 * trigger.256 *257 * To avoid memory allocations at runtime, the necessary memory is allocated258 * when the extended error reporting is enabled for a device (by259 * dasd_eer_probe). There is one sense subsystem status request for each260 * eer enabled DASD device. The presence of the cqr in device->eer_cqr261 * indicates that eer is enable for the device. The use of the snss request262 * is protected by the DASD_FLAG_EER_IN_USE bit. When this flag indicates263 * that the cqr is currently in use, dasd_eer_snss cannot start a second264 * request but sets the DASD_FLAG_EER_SNSS flag instead. The callback of265 * the SNSS request will check the bit and call dasd_eer_snss again.266 */267 268#define SNSS_DATA_SIZE 44269 270#define DASD_EER_BUSID_SIZE 10271struct dasd_eer_header {272	__u32 total_size;273	__u32 trigger;274	__u64 tv_sec;275	__u64 tv_usec;276	char busid[DASD_EER_BUSID_SIZE];277} __attribute__ ((packed));278 279/*280 * The following function can be used for those triggers that have281 * all necessary data available when the function is called.282 * If the parameter cqr is not NULL, the chain of requests will be searched283 * for valid sense data, and all valid sense data sets will be added to284 * the triggers data.285 */286static void dasd_eer_write_standard_trigger(struct dasd_device *device,287					    struct dasd_ccw_req *cqr,288					    int trigger)289{290	struct dasd_ccw_req *temp_cqr;291	int data_size;292	struct timespec64 ts;293	struct dasd_eer_header header;294	unsigned long flags;295	struct eerbuffer *eerb;296	char *sense;297 298	/* go through cqr chain and count the valid sense data sets */299	data_size = 0;300	for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers)301		if (dasd_get_sense(&temp_cqr->irb))302			data_size += 32;303 304	header.total_size = sizeof(header) + data_size + 4; /* "EOR" */305	header.trigger = trigger;306	ktime_get_real_ts64(&ts);307	header.tv_sec = ts.tv_sec;308	header.tv_usec = ts.tv_nsec / NSEC_PER_USEC;309	strscpy(header.busid, dev_name(&device->cdev->dev),310		DASD_EER_BUSID_SIZE);311 312	spin_lock_irqsave(&bufferlock, flags);313	list_for_each_entry(eerb, &bufferlist, list) {314		dasd_eer_start_record(eerb, header.total_size);315		dasd_eer_write_buffer(eerb, (char *) &header, sizeof(header));316		for (temp_cqr = cqr; temp_cqr; temp_cqr = temp_cqr->refers) {317			sense = dasd_get_sense(&temp_cqr->irb);318			if (sense)319				dasd_eer_write_buffer(eerb, sense, 32);320		}321		dasd_eer_write_buffer(eerb, "EOR", 4);322	}323	spin_unlock_irqrestore(&bufferlock, flags);324	wake_up_interruptible(&dasd_eer_read_wait_queue);325}326 327/*328 * This function writes a DASD_EER_STATECHANGE trigger.329 */330static void dasd_eer_write_snss_trigger(struct dasd_device *device,331					struct dasd_ccw_req *cqr,332					int trigger)333{334	int data_size;335	int snss_rc;336	struct timespec64 ts;337	struct dasd_eer_header header;338	unsigned long flags;339	struct eerbuffer *eerb;340 341	snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;342	if (snss_rc)343		data_size = 0;344	else345		data_size = SNSS_DATA_SIZE;346 347	header.total_size = sizeof(header) + data_size + 4; /* "EOR" */348	header.trigger = DASD_EER_STATECHANGE;349	ktime_get_real_ts64(&ts);350	header.tv_sec = ts.tv_sec;351	header.tv_usec = ts.tv_nsec / NSEC_PER_USEC;352	strscpy(header.busid, dev_name(&device->cdev->dev),353		DASD_EER_BUSID_SIZE);354 355	spin_lock_irqsave(&bufferlock, flags);356	list_for_each_entry(eerb, &bufferlist, list) {357		dasd_eer_start_record(eerb, header.total_size);358		dasd_eer_write_buffer(eerb, (char *) &header , sizeof(header));359		if (!snss_rc)360			dasd_eer_write_buffer(eerb, cqr->data, SNSS_DATA_SIZE);361		dasd_eer_write_buffer(eerb, "EOR", 4);362	}363	spin_unlock_irqrestore(&bufferlock, flags);364	wake_up_interruptible(&dasd_eer_read_wait_queue);365}366 367/*368 * This function is called for all triggers. It calls the appropriate369 * function that writes the actual trigger records.370 */371void dasd_eer_write(struct dasd_device *device, struct dasd_ccw_req *cqr,372		    unsigned int id)373{374	if (!device->eer_cqr)375		return;376	switch (id) {377	case DASD_EER_FATALERROR:378	case DASD_EER_PPRCSUSPEND:379		dasd_eer_write_standard_trigger(device, cqr, id);380		break;381	case DASD_EER_NOPATH:382	case DASD_EER_NOSPC:383	case DASD_EER_AUTOQUIESCE:384		dasd_eer_write_standard_trigger(device, NULL, id);385		break;386	case DASD_EER_STATECHANGE:387		dasd_eer_write_snss_trigger(device, cqr, id);388		break;389	default: /* unknown trigger, so we write it without any sense data */390		dasd_eer_write_standard_trigger(device, NULL, id);391		break;392	}393}394EXPORT_SYMBOL(dasd_eer_write);395 396/*397 * Start a sense subsystem status request.398 * Needs to be called with the device held.399 */400void dasd_eer_snss(struct dasd_device *device)401{402	struct dasd_ccw_req *cqr;403 404	cqr = device->eer_cqr;405	if (!cqr)	/* Device not eer enabled. */406		return;407	if (test_and_set_bit(DASD_FLAG_EER_IN_USE, &device->flags)) {408		/* Sense subsystem status request in use. */409		set_bit(DASD_FLAG_EER_SNSS, &device->flags);410		return;411	}412	/* cdev is already locked, can't use dasd_add_request_head */413	clear_bit(DASD_FLAG_EER_SNSS, &device->flags);414	cqr->status = DASD_CQR_QUEUED;415	list_add(&cqr->devlist, &device->ccw_queue);416	dasd_schedule_device_bh(device);417}418 419/*420 * Callback function for use with sense subsystem status request.421 */422static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data)423{424	struct dasd_device *device = cqr->startdev;425	unsigned long flags;426 427	dasd_eer_write(device, cqr, DASD_EER_STATECHANGE);428	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);429	if (device->eer_cqr == cqr) {430		clear_bit(DASD_FLAG_EER_IN_USE, &device->flags);431		if (test_bit(DASD_FLAG_EER_SNSS, &device->flags))432			/* Another SNSS has been requested in the meantime. */433			dasd_eer_snss(device);434		cqr = NULL;435	}436	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);437	if (cqr)438		/*439		 * Extended error recovery has been switched off while440		 * the SNSS request was running. It could even have441		 * been switched off and on again in which case there442		 * is a new ccw in device->eer_cqr. Free the "old"443		 * snss request now.444		 */445		dasd_sfree_request(cqr, device);446}447 448/*449 * Enable error reporting on a given device.450 */451int dasd_eer_enable(struct dasd_device *device)452{453	struct dasd_ccw_req *cqr = NULL;454	unsigned long flags;455	struct ccw1 *ccw;456	int rc = 0;457 458	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);459	if (device->eer_cqr)460		goto out;461	else if (!device->discipline ||462		 strcmp(device->discipline->name, "ECKD"))463		rc = -EMEDIUMTYPE;464	else if (test_bit(DASD_FLAG_OFFLINE, &device->flags))465		rc = -EBUSY;466 467	if (rc)468		goto out;469 470	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */,471				   SNSS_DATA_SIZE, device, NULL);472	if (IS_ERR(cqr)) {473		rc = -ENOMEM;474		cqr = NULL;475		goto out;476	}477 478	cqr->startdev = device;479	cqr->retries = 255;480	cqr->expires = 10 * HZ;481	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);482	set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags);483 484	ccw = cqr->cpaddr;485	ccw->cmd_code = DASD_ECKD_CCW_SNSS;486	ccw->count = SNSS_DATA_SIZE;487	ccw->flags = 0;488	ccw->cda = virt_to_dma32(cqr->data);489 490	cqr->buildclk = get_tod_clock();491	cqr->status = DASD_CQR_FILLED;492	cqr->callback = dasd_eer_snss_cb;493 494	if (!device->eer_cqr) {495		device->eer_cqr = cqr;496		cqr = NULL;497	}498 499out:500	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);501 502	if (cqr)503		dasd_sfree_request(cqr, device);504 505	return rc;506}507 508/*509 * Disable error reporting on a given device.510 */511void dasd_eer_disable(struct dasd_device *device)512{513	struct dasd_ccw_req *cqr;514	unsigned long flags;515	int in_use;516 517	if (!device->eer_cqr)518		return;519	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);520	cqr = device->eer_cqr;521	device->eer_cqr = NULL;522	clear_bit(DASD_FLAG_EER_SNSS, &device->flags);523	in_use = test_and_clear_bit(DASD_FLAG_EER_IN_USE, &device->flags);524	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);525	if (cqr && !in_use)526		dasd_sfree_request(cqr, device);527}528 529/*530 * SECTION: the device operations531 */532 533/*534 * On the one side we need a lock to access our internal buffer, on the535 * other side a copy_to_user can sleep. So we need to copy the data we have536 * to transfer in a readbuffer, which is protected by the readbuffer_mutex.537 */538static char readbuffer[PAGE_SIZE];539static DEFINE_MUTEX(readbuffer_mutex);540 541static int dasd_eer_open(struct inode *inp, struct file *filp)542{543	struct eerbuffer *eerb;544	unsigned long flags;545 546	eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);547	if (!eerb)548		return -ENOMEM;549	eerb->buffer_page_count = eer_pages;550	if (eerb->buffer_page_count < 1 ||551	    eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {552		kfree(eerb);553		DBF_EVENT(DBF_WARNING, "can't open device since module "554			"parameter eer_pages is smaller than 1 or"555			" bigger than %d", (int)(INT_MAX / PAGE_SIZE));556		return -EINVAL;557	}558	eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;559	eerb->buffer = kmalloc_array(eerb->buffer_page_count, sizeof(char *),560				     GFP_KERNEL);561        if (!eerb->buffer) {562		kfree(eerb);563                return -ENOMEM;564	}565	if (dasd_eer_allocate_buffer_pages(eerb->buffer,566					   eerb->buffer_page_count)) {567		kfree(eerb->buffer);568		kfree(eerb);569		return -ENOMEM;570	}571	filp->private_data = eerb;572	spin_lock_irqsave(&bufferlock, flags);573	list_add(&eerb->list, &bufferlist);574	spin_unlock_irqrestore(&bufferlock, flags);575 576	return nonseekable_open(inp,filp);577}578 579static int dasd_eer_close(struct inode *inp, struct file *filp)580{581	struct eerbuffer *eerb;582	unsigned long flags;583 584	eerb = (struct eerbuffer *) filp->private_data;585	spin_lock_irqsave(&bufferlock, flags);586	list_del(&eerb->list);587	spin_unlock_irqrestore(&bufferlock, flags);588	dasd_eer_free_buffer_pages(eerb->buffer, eerb->buffer_page_count);589	kfree(eerb->buffer);590	kfree(eerb);591 592	return 0;593}594 595static ssize_t dasd_eer_read(struct file *filp, char __user *buf,596			     size_t count, loff_t *ppos)597{598	int tc,rc;599	int tailcount,effective_count;600        unsigned long flags;601	struct eerbuffer *eerb;602 603	eerb = (struct eerbuffer *) filp->private_data;604	if (mutex_lock_interruptible(&readbuffer_mutex))605		return -ERESTARTSYS;606 607	spin_lock_irqsave(&bufferlock, flags);608 609	if (eerb->residual < 0) { /* the remainder of this record */610		                  /* has been deleted             */611		eerb->residual = 0;612		spin_unlock_irqrestore(&bufferlock, flags);613		mutex_unlock(&readbuffer_mutex);614		return -EIO;615	} else if (eerb->residual > 0) {616		/* OK we still have a second half of a record to deliver */617		effective_count = min(eerb->residual, (int) count);618		eerb->residual -= effective_count;619	} else {620		tc = 0;621		while (!tc) {622			tc = dasd_eer_read_buffer(eerb, (char *) &tailcount,623						  sizeof(tailcount));624			if (!tc) {625				/* no data available */626				spin_unlock_irqrestore(&bufferlock, flags);627				mutex_unlock(&readbuffer_mutex);628				if (filp->f_flags & O_NONBLOCK)629					return -EAGAIN;630				rc = wait_event_interruptible(631					dasd_eer_read_wait_queue,632					eerb->head != eerb->tail);633				if (rc)634					return rc;635				if (mutex_lock_interruptible(&readbuffer_mutex))636					return -ERESTARTSYS;637				spin_lock_irqsave(&bufferlock, flags);638			}639		}640		WARN_ON(tc != sizeof(tailcount));641		effective_count = min(tailcount,(int)count);642		eerb->residual = tailcount - effective_count;643	}644 645	tc = dasd_eer_read_buffer(eerb, readbuffer, effective_count);646	WARN_ON(tc != effective_count);647 648	spin_unlock_irqrestore(&bufferlock, flags);649 650	if (copy_to_user(buf, readbuffer, effective_count)) {651		mutex_unlock(&readbuffer_mutex);652		return -EFAULT;653	}654 655	mutex_unlock(&readbuffer_mutex);656	return effective_count;657}658 659static __poll_t dasd_eer_poll(struct file *filp, poll_table *ptable)660{661	__poll_t mask;662	unsigned long flags;663	struct eerbuffer *eerb;664 665	eerb = (struct eerbuffer *) filp->private_data;666	poll_wait(filp, &dasd_eer_read_wait_queue, ptable);667	spin_lock_irqsave(&bufferlock, flags);668	if (eerb->head != eerb->tail)669		mask = EPOLLIN | EPOLLRDNORM ;670	else671		mask = 0;672	spin_unlock_irqrestore(&bufferlock, flags);673	return mask;674}675 676static const struct file_operations dasd_eer_fops = {677	.open		= &dasd_eer_open,678	.release	= &dasd_eer_close,679	.read		= &dasd_eer_read,680	.poll		= &dasd_eer_poll,681	.owner		= THIS_MODULE,682	.llseek		= noop_llseek,683};684 685static struct miscdevice *dasd_eer_dev = NULL;686 687int __init dasd_eer_init(void)688{689	int rc;690 691	dasd_eer_dev = kzalloc(sizeof(*dasd_eer_dev), GFP_KERNEL);692	if (!dasd_eer_dev)693		return -ENOMEM;694 695	dasd_eer_dev->minor = MISC_DYNAMIC_MINOR;696	dasd_eer_dev->name  = "dasd_eer";697	dasd_eer_dev->fops  = &dasd_eer_fops;698 699	rc = misc_register(dasd_eer_dev);700	if (rc) {701		kfree(dasd_eer_dev);702		dasd_eer_dev = NULL;703		DBF_EVENT(DBF_ERR, "%s", "dasd_eer_init could not "704		       "register misc device");705		return rc;706	}707 708	return 0;709}710 711void dasd_eer_exit(void)712{713	if (dasd_eer_dev) {714		misc_deregister(dasd_eer_dev);715		kfree(dasd_eer_dev);716		dasd_eer_dev = NULL;717	}718}719