📄 tape_34xx.c
字号:
struct list_head * sbid_list; struct tape_34xx_sbid * sbid; struct list_head * l; /* * immediately return if there is no list at all or the block to add * is located in segment 1 of wrap 0 because this position is used * if no hardware position data is supplied. */ sbid_list = (struct list_head *) device->discdata; if (!sbid_list || (bid.segment < 2 && bid.wrap == 0)) return; /* * Search the position where to insert the new entry. Hardware * acceleration uses only the segment and wrap number. So we * need only one entry for a specific wrap/segment combination. * If there is a block with a lower number but the same hard- * ware position data we just update the block number in the * existing entry. */ list_for_each(l, sbid_list) { sbid = list_entry(l, struct tape_34xx_sbid, list); if ( (sbid->bid.segment == bid.segment) && (sbid->bid.wrap == bid.wrap) ) { if (bid.block < sbid->bid.block) sbid->bid = bid; else return; break; } /* Sort in according to logical block number. */ if (bid.block < sbid->bid.block) { tape_34xx_append_new_sbid(bid, l->prev); break; } } /* List empty or new block bigger than last entry. */ if (l == sbid_list) tape_34xx_append_new_sbid(bid, l->prev); DBF_LH(4, "Current list is:\n"); list_for_each(l, sbid_list) { sbid = list_entry(l, struct tape_34xx_sbid, list); DBF_LH(4, "%d:%03d@%05d\n", sbid->bid.wrap, sbid->bid.segment, sbid->bid.block ); }}/* * Delete all entries from the search block ID list that belong to tape blocks * equal or higher than the given number. */static voidtape_34xx_delete_sbid_from(struct tape_device *device, int from){ struct list_head * sbid_list; struct tape_34xx_sbid * sbid; struct list_head * l; struct list_head * n; sbid_list = (struct list_head *) device->discdata; if (!sbid_list) return; list_for_each_safe(l, n, sbid_list) { sbid = list_entry(l, struct tape_34xx_sbid, list); if (sbid->bid.block >= from) { DBF_LH(4, "Delete sbid %d:%03d@%05d\n", sbid->bid.wrap, sbid->bid.segment, sbid->bid.block ); list_del(l); kfree(sbid); } }}/* * Merge hardware position data into a block id. */static voidtape_34xx_merge_sbid( struct tape_device * device, struct tape_34xx_block_id * bid) { struct tape_34xx_sbid * sbid; struct tape_34xx_sbid * sbid_to_use; struct list_head * sbid_list; struct list_head * l; sbid_list = (struct list_head *) device->discdata; bid->wrap = 0; bid->segment = 1; if (!sbid_list || list_empty(sbid_list)) return; sbid_to_use = NULL; list_for_each(l, sbid_list) { sbid = list_entry(l, struct tape_34xx_sbid, list); if (sbid->bid.block >= bid->block) break; sbid_to_use = sbid; } if (sbid_to_use) { bid->wrap = sbid_to_use->bid.wrap; bid->segment = sbid_to_use->bid.segment; DBF_LH(4, "Use %d:%03d@%05d for %05d\n", sbid_to_use->bid.wrap, sbid_to_use->bid.segment, sbid_to_use->bid.block, bid->block ); }}static inttape_34xx_setup_device(struct tape_device * device){ int rc; struct list_head * discdata; DBF_EVENT(6, "34xx device setup\n"); if ((rc = tape_std_assign(device)) == 0) { if ((rc = tape_34xx_medium_sense(device)) != 0) { DBF_LH(3, "34xx medium sense returned %d\n", rc); } } discdata = kmalloc(sizeof(struct list_head), GFP_KERNEL); if (discdata) { INIT_LIST_HEAD(discdata); device->discdata = discdata; } return rc;}static voidtape_34xx_cleanup_device(struct tape_device *device){ tape_std_unassign(device); if (device->discdata) { tape_34xx_delete_sbid_from(device, 0); kfree(device->discdata); device->discdata = NULL; }}/* * MTTELL: Tell block. Return the number of block relative to current file. */static inttape_34xx_mttell(struct tape_device *device, int mt_count){ struct { struct tape_34xx_block_id cbid; struct tape_34xx_block_id dbid; } __attribute__ ((packed)) block_id; int rc; rc = tape_std_read_block_id(device, (__u64 *) &block_id); if (rc) return rc; tape_34xx_add_sbid(device, block_id.cbid); return block_id.cbid.block;}/* * MTSEEK: seek to the specified block. */static inttape_34xx_mtseek(struct tape_device *device, int mt_count){ struct tape_request *request; struct tape_34xx_block_id * bid; if (mt_count > 0x3fffff) { DBF_EXCEPTION(6, "xsee parm\n"); return -EINVAL; } request = tape_alloc_request(3, 4); if (IS_ERR(request)) return PTR_ERR(request); /* setup ccws */ request->op = TO_LBL; bid = (struct tape_34xx_block_id *) request->cpdata; bid->format = (*device->modeset_byte & 0x08) ? TAPE34XX_FMT_3480_XF : TAPE34XX_FMT_3480; bid->block = mt_count; tape_34xx_merge_sbid(device, bid); tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); /* execute it */ return tape_do_io_free(device, request);}#ifdef CONFIG_S390_TAPE_BLOCK/* * Tape block read for 34xx. */static struct tape_request *tape_34xx_bread(struct tape_device *device, struct request *req){ struct tape_request *request; struct ccw1 *ccw; int count = 0, i; unsigned off; char *dst; struct bio_vec *bv; struct bio *bio; struct tape_34xx_block_id * start_block; DBF_EVENT(6, "xBREDid:"); /* Count the number of blocks for the request. */ rq_for_each_bio(bio, req) { bio_for_each_segment(bv, bio, i) { count += bv->bv_len >> (TAPEBLOCK_HSEC_S2B + 9); } } /* Allocate the ccw request. */ request = tape_alloc_request(3+count+1, 8); if (IS_ERR(request)) return request; /* Setup ccws. */ request->op = TO_BLOCK; start_block = (struct tape_34xx_block_id *) request->cpdata; start_block->block = req->sector >> TAPEBLOCK_HSEC_S2B; DBF_EVENT(6, "start_block = %i\n", start_block->block); ccw = request->cpaddr; ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte); /* * We always setup a nop after the mode set ccw. This slot is * used in tape_std_check_locate to insert a locate ccw if the * current tape position doesn't match the start block to be read. * The second nop will be filled with a read block id which is in * turn used by tape_34xx_free_bread to populate the segment bid * table. */ ccw = tape_ccw_cc(ccw, NOP, 0, NULL); ccw = tape_ccw_cc(ccw, NOP, 0, NULL); rq_for_each_bio(bio, req) { bio_for_each_segment(bv, bio, i) { dst = kmap(bv->bv_page) + bv->bv_offset; for (off = 0; off < bv->bv_len; off += TAPEBLOCK_HSEC_SIZE) { ccw->flags = CCW_FLAG_CC; ccw->cmd_code = READ_FORWARD; ccw->count = TAPEBLOCK_HSEC_SIZE; set_normalized_cda(ccw, (void*) __pa(dst)); ccw++; dst += TAPEBLOCK_HSEC_SIZE; } } } ccw = tape_ccw_end(ccw, NOP, 0, NULL); DBF_EVENT(6, "xBREDccwg\n"); return request;}static voidtape_34xx_free_bread (struct tape_request *request){ struct ccw1* ccw; ccw = request->cpaddr; if ((ccw + 2)->cmd_code == READ_BLOCK_ID) { struct { struct tape_34xx_block_id cbid; struct tape_34xx_block_id dbid; } __attribute__ ((packed)) *rbi_data; rbi_data = request->cpdata; if (request->device) tape_34xx_add_sbid(request->device, rbi_data->cbid); } /* Last ccw is a nop and doesn't need clear_normalized_cda */ for (; ccw->flags & CCW_FLAG_CC; ccw++) if (ccw->cmd_code == READ_FORWARD) clear_normalized_cda(ccw); tape_free_request(request);}/* * check_locate is called just before the tape request is passed to * the common io layer for execution. It has to check the current * tape position and insert a locate ccw if it doesn't match the * start block for the request. */static voidtape_34xx_check_locate(struct tape_device *device, struct tape_request *request){ struct tape_34xx_block_id * start_block; start_block = (struct tape_34xx_block_id *) request->cpdata; if (start_block->block == device->blk_data.block_position) return; DBF_LH(4, "Block seek(%06d+%06d)\n", start_block->block, device->bof); start_block->wrap = 0; start_block->segment = 1; start_block->format = (*device->modeset_byte & 0x08) ? TAPE34XX_FMT_3480_XF : TAPE34XX_FMT_3480; start_block->block = start_block->block + device->bof; tape_34xx_merge_sbid(device, start_block); tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); tape_ccw_cc(request->cpaddr + 2, READ_BLOCK_ID, 8, request->cpdata);}#endif/* * List of 3480/3490 magnetic tape commands. */static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { [MTRESET] = tape_std_mtreset, [MTFSF] = tape_std_mtfsf, [MTBSF] = tape_std_mtbsf, [MTFSR] = tape_std_mtfsr, [MTBSR] = tape_std_mtbsr, [MTWEOF] = tape_std_mtweof, [MTREW] = tape_std_mtrew, [MTOFFL] = tape_std_mtoffl, [MTNOP] = tape_std_mtnop, [MTRETEN] = tape_std_mtreten, [MTBSFM] = tape_std_mtbsfm, [MTFSFM] = tape_std_mtfsfm, [MTEOM] = tape_std_mteom, [MTERASE] = tape_std_mterase, [MTRAS1] = NULL, [MTRAS2] = NULL, [MTRAS3] = NULL, [MTSETBLK] = tape_std_mtsetblk, [MTSETDENSITY] = NULL, [MTSEEK] = tape_34xx_mtseek, [MTTELL] = tape_34xx_mttell, [MTSETDRVBUFFER] = NULL, [MTFSS] = NULL, [MTBSS] = NULL, [MTWSM] = NULL, [MTLOCK] = NULL, [MTUNLOCK] = NULL, [MTLOAD] = tape_std_mtload, [MTUNLOAD] = tape_std_mtunload, [MTCOMPRESSION] = tape_std_mtcompression, [MTSETPART] = NULL, [MTMKPART] = NULL};/* * Tape discipline structure for 3480 and 3490. */static struct tape_discipline tape_discipline_34xx = { .owner = THIS_MODULE, .setup_device = tape_34xx_setup_device, .cleanup_device = tape_34xx_cleanup_device, .process_eov = tape_std_process_eov, .irq = tape_34xx_irq, .read_block = tape_std_read_block, .write_block = tape_std_write_block,#ifdef CONFIG_S390_TAPE_BLOCK .bread = tape_34xx_bread, .free_bread = tape_34xx_free_bread, .check_locate = tape_34xx_check_locate,#endif .ioctl_fn = tape_34xx_ioctl, .mtop_array = tape_34xx_mtop};static struct ccw_device_id tape_34xx_ids[] = { { CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), driver_info: tape_3480}, { CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), driver_info: tape_3490}, { /* end of list */ }};static inttape_34xx_online(struct ccw_device *cdev){ return tape_generic_online( cdev->dev.driver_data, &tape_discipline_34xx );}static inttape_34xx_offline(struct ccw_device *cdev){ return tape_generic_offline(cdev->dev.driver_data);}static struct ccw_driver tape_34xx_driver = { .name = "tape_34xx", .owner = THIS_MODULE, .ids = tape_34xx_ids, .probe = tape_generic_probe, .remove = tape_generic_remove, .set_online = tape_34xx_online, .set_offline = tape_34xx_offline,};static inttape_34xx_init (void){ int rc; TAPE_DBF_AREA = debug_register ( "tape_34xx", 2, 2, 4*sizeof(long)); debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);#ifdef DBF_LIKE_HELL debug_set_level(TAPE_DBF_AREA, 6);#endif DBF_EVENT(3, "34xx init: $Revision: 1.23 $\n"); /* Register driver for 3480/3490 tapes. */ rc = ccw_driver_register(&tape_34xx_driver); if (rc) DBF_EVENT(3, "34xx init failed\n"); else DBF_EVENT(3, "34xx registered\n"); return rc;}static voidtape_34xx_exit(void){ ccw_driver_unregister(&tape_34xx_driver); debug_unregister(TAPE_DBF_AREA);}MODULE_DEVICE_TABLE(ccw, tape_34xx_ids);MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape " "device driver ($Revision: 1.23 $)");MODULE_LICENSE("GPL");module_init(tape_34xx_init);module_exit(tape_34xx_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -