📄 tape_34xx.c
字号:
/* * 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 + -