⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tape_34xx.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  drivers/s390/char/tape_34xx.c *    tape device discipline for 3480/3490 tapes. * *  S390 and zSeries version *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation *    Author(s): Carsten Otte <cotte@de.ibm.com> *		 Tuan Ngo-Anh <ngoanh@de.ibm.com> *		 Martin Schwidefsky <schwidefsky@de.ibm.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/bio.h>#include <linux/workqueue.h>#define TAPE_DBF_AREA	tape_34xx_dbf#include "tape.h"#include "tape_std.h"#define PRINTK_HEADER "TAPE_34XX: "/* * Pointer to debug area. */debug_info_t *TAPE_DBF_AREA = NULL;EXPORT_SYMBOL(TAPE_DBF_AREA);enum tape_34xx_type {	tape_3480,	tape_3490,};#define TAPE34XX_FMT_3480	0#define TAPE34XX_FMT_3480_2_XF	1#define TAPE34XX_FMT_3480_XF	2struct tape_34xx_block_id {	unsigned int	wrap		: 1;	unsigned int	segment		: 7;	unsigned int	format		: 2;	unsigned int	block		: 22;};/* * A list of block ID's is used to faster seek blocks. */struct tape_34xx_sbid {	struct list_head		list;	struct tape_34xx_block_id	bid;};static void tape_34xx_delete_sbid_from(struct tape_device *, int);/* * Medium sense for 34xx tapes. There is no 'real' medium sense call. * So we just do a normal sense. */static inttape_34xx_medium_sense(struct tape_device *device){	struct tape_request *request;	unsigned char       *sense;	int                  rc;	request = tape_alloc_request(1, 32);	if (IS_ERR(request)) {		DBF_EXCEPTION(6, "MSEN fail\n");		return PTR_ERR(request);	}	request->op = TO_MSEN;	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);	rc = tape_do_io_interruptible(device, request);	if (request->rc == 0) {		sense = request->cpdata;		/*		 * This isn't quite correct. But since INTERVENTION_REQUIRED		 * means that the drive is 'neither ready nor on-line' it is		 * only slightly inaccurate to say there is no tape loaded if		 * the drive isn't online...		 */		if (sense[0] & SENSE_INTERVENTION_REQUIRED)			tape_med_state_set(device, MS_UNLOADED);		else			tape_med_state_set(device, MS_LOADED);		if (sense[1] & SENSE_WRITE_PROTECT)			device->tape_generic_status |= GMT_WR_PROT(~0);		else			device->tape_generic_status &= ~GMT_WR_PROT(~0);	} else {		DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n",			request->rc);	}	tape_free_request(request);	return rc;}/* * These functions are currently used only to schedule a medium_sense for * later execution. This is because we get an interrupt whenever a medium * is inserted but cannot call tape_do_io* from an interrupt context. * Maybe that's useful for other actions we want to start from the * interrupt handler. */static voidtape_34xx_work_handler(void *data){	struct {		struct tape_device	*device;		enum tape_op		 op;		struct work_struct	 work;	} *p = data;	switch(p->op) {		case TO_MSEN:			tape_34xx_medium_sense(p->device);			break;		default:			DBF_EVENT(3, "T34XX: internal error: unknown work\n");	}	p->device = tape_put_device(p->device);	kfree(p);}static inttape_34xx_schedule_work(struct tape_device *device, enum tape_op op){	struct {		struct tape_device	*device;		enum tape_op		 op;		struct work_struct	 work;	} *p;	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)		return -ENOMEM;	memset(p, 0, sizeof(*p));	INIT_WORK(&p->work, tape_34xx_work_handler, p);	p->device = tape_get_device_reference(device);	p->op     = op;	schedule_work(&p->work);	return 0;}/* * Done Handler is called when dev stat = DEVICE-END (successful operation) */static inline inttape_34xx_done(struct tape_request *request){	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);	switch (request->op) {		case TO_DSE:		case TO_RUN:		case TO_WRI:		case TO_WTM:		case TO_ASSIGN:		case TO_UNASSIGN:			tape_34xx_delete_sbid_from(request->device, 0);			break;		default:			;	}	return TAPE_IO_SUCCESS;}static inline inttape_34xx_erp_failed(struct tape_request *request, int rc){	DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n",		  tape_op_verbose[request->op], rc);	return rc;}static inline inttape_34xx_erp_succeeded(struct tape_request *request){	DBF_EVENT(3, "Error Recovery successful for %s\n",		  tape_op_verbose[request->op]);	return tape_34xx_done(request);}static inline inttape_34xx_erp_retry(struct tape_request *request){	DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]);	return TAPE_IO_RETRY;}/* * This function is called, when no request is outstanding and we get an * interrupt */static inttape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb){	if (irb->scsw.dstat == 0x85 /* READY */) {		/* A medium was inserted in the drive. */		DBF_EVENT(6, "xuud med\n");		tape_34xx_delete_sbid_from(device, 0);		tape_34xx_schedule_work(device, TO_MSEN);	} else {		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);		PRINT_WARN("Unsolicited IRQ (Device End) caught.\n");		tape_dump_sense(device, NULL, irb);	}	return TAPE_IO_SUCCESS;}/* * Read Opposite Error Recovery Function: * Used, when Read Forward does not work */static inttape_34xx_erp_read_opposite(struct tape_device *device,			    struct tape_request *request){	if (request->op == TO_RFO) {		/*		 * We did read forward, but the data could not be read		 * *correctly*. We transform the request to a read backward		 * and try again.		 */		tape_std_read_backward(device, request);		return tape_34xx_erp_retry(request);	}	if (request->op != TO_RBA)		PRINT_ERR("read_opposite called with state:%s\n",			  tape_op_verbose[request->op]);	/*	 * We tried to read forward and backward, but hat no	 * success -> failed.	 */	return tape_34xx_erp_failed(request, -EIO);}static inttape_34xx_erp_bug(struct tape_device *device, struct tape_request *request,		  struct irb *irb, int no){	if (request->op != TO_ASSIGN) {		PRINT_WARN("An unexpected condition #%d was caught in "			   "tape error recovery.\n", no);		PRINT_WARN("Please report this incident.\n");		if (request)			PRINT_WARN("Operation of tape:%s\n",				   tape_op_verbose[request->op]);		tape_dump_sense(device, request, irb);	}	return tape_34xx_erp_failed(request, -EIO);}/* * Handle data overrun between cu and drive. The channel speed might * be too slow. */static inttape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request,		      struct irb *irb){	if (irb->ecw[3] == 0x40) {		PRINT_WARN ("Data overrun error between control-unit "			    "and drive. Use a faster channel connection, "			    "if possible! \n");		return tape_34xx_erp_failed(request, -EIO);	}	return tape_34xx_erp_bug(device, request, irb, -1);}/* * Handle record sequence error. */static inttape_34xx_erp_sequence(struct tape_device *device,		       struct tape_request *request, struct irb *irb){	if (irb->ecw[3] == 0x41) {		/*		 * cu detected incorrect block-id sequence on tape.		 */		PRINT_WARN("Illegal block-id sequence found!\n");		return tape_34xx_erp_failed(request, -EIO);	}	/*	 * Record sequence error bit is set, but erpa does not	 * show record sequence error.	 */	return tape_34xx_erp_bug(device, request, irb, -2);}/* * This function analyses the tape's sense-data in case of a unit-check. * If possible, it tries to recover from the error. Else the user is * informed about the problem. */static inttape_34xx_unit_check(struct tape_device *device, struct tape_request *request,		     struct irb *irb){	int inhibit_cu_recovery;	__u8* sense;	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;	sense = irb->ecw;#ifdef CONFIG_S390_TAPE_BLOCK	if (request->op == TO_BLOCK) {		/*		 * Recovery for block device requests. Set the block_position		 * to something invalid and retry.		 */		device->blk_data.block_position = -1;		if (request->retries-- <= 0)			return tape_34xx_erp_failed(request, -EIO);		else			return tape_34xx_erp_retry(request);	}#endif	if (		sense[0] & SENSE_COMMAND_REJECT &&		sense[1] & SENSE_WRITE_PROTECT	) {		if (			request->op == TO_DSE ||			request->op == TO_WRI ||			request->op == TO_WTM		) {			/* medium is write protected */			return tape_34xx_erp_failed(request, -EACCES);		} else {			return tape_34xx_erp_bug(device, request, irb, -3);		}	}	/*	 * Special cases for various tape-states when reaching	 * end of recorded area	 *	 * FIXME: Maybe a special case of the special case:	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&	 *        sense[1] == SENSE_DRIVE_ONLINE    &&	 *        sense[3] == 0x47 (Volume Fenced)	 *	 *        This was caused by continued FSF or FSR after an	 *        'End Of Data'.	 */	if ((		sense[0] == SENSE_DATA_CHECK      ||		sense[0] == SENSE_EQUIPMENT_CHECK ||		sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK	) && (		sense[1] == SENSE_DRIVE_ONLINE ||		sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE	)) {		switch (request->op) {		/*		 * sense[0] == SENSE_DATA_CHECK   &&		 * sense[1] == SENSE_DRIVE_ONLINE		 * sense[3] == 0x36 (End Of Data)		 *		 * Further seeks might return a 'Volume Fenced'.		 */		case TO_FSF:		case TO_FSB:			/* Trying to seek beyond end of recorded area */			return tape_34xx_erp_failed(request, -ENOSPC);		case TO_BSB:			return tape_34xx_erp_retry(request);		/*		 * sense[0] == SENSE_DATA_CHECK   &&		 * sense[1] == SENSE_DRIVE_ONLINE &&		 * sense[3] == 0x36 (End Of Data)		 */		case TO_LBL:			/* Block could not be located. */			tape_34xx_delete_sbid_from(device, 0);			return tape_34xx_erp_failed(request, -EIO);		case TO_RFO:			/* Read beyond end of recorded area -> 0 bytes read */			return tape_34xx_erp_failed(request, 0);		/*		 * sense[0] == SENSE_EQUIPMENT_CHECK &&		 * sense[1] == SENSE_DRIVE_ONLINE    &&		 * sense[3] == 0x38 (Physical End Of Volume)		 */		case TO_WRI:			/* Writing at physical end of volume */			return tape_34xx_erp_failed(request, -ENOSPC);		default:			PRINT_ERR("Invalid op in %s:%i\n",				  __FUNCTION__, __LINE__);			return tape_34xx_erp_failed(request, 0);		}	}	/* Sensing special bits */	if (sense[0] & SENSE_BUS_OUT_CHECK)		return tape_34xx_erp_retry(request);	if (sense[0] & SENSE_DATA_CHECK) {		/*		 * hardware failure, damaged tape or improper		 * operating conditions		 */		switch (sense[3]) {		case 0x23:			/* a read data check occurred */			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||			    inhibit_cu_recovery)				// data check is not permanent, may be				// recovered. We always use async-mode with				// cu-recovery, so this should *never* happen.				return tape_34xx_erp_bug(device, request,							 irb, -4);			/* data check is permanent, CU recovery has failed */			PRINT_WARN("Permanent read error\n");			return tape_34xx_erp_failed(request, -EIO);		case 0x25:			// a write data check occurred			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||			    inhibit_cu_recovery)				// data check is not permanent, may be				// recovered. We always use async-mode with				// cu-recovery, so this should *never* happen.				return tape_34xx_erp_bug(device, request,							 irb, -5);			// data check is permanent, cu-recovery has failed			PRINT_WARN("Permanent write error\n");			return tape_34xx_erp_failed(request, -EIO);		case 0x26:			/* Data Check (read opposite) occurred. */			return tape_34xx_erp_read_opposite(device, request);		case 0x28:			/* ID-Mark at tape start couldn't be written */			PRINT_WARN("ID-Mark could not be written.\n");			return tape_34xx_erp_failed(request, -EIO);		case 0x31:			/* Tape void. Tried to read beyond end of device. */			PRINT_WARN("Read beyond end of recorded area.\n");			return tape_34xx_erp_failed(request, -ENOSPC);		case 0x41:			/* Record sequence error. */			PRINT_WARN("Invalid block-id sequence found.\n");			return tape_34xx_erp_failed(request, -EIO);		default:

⌨️ 快捷键说明

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