提交 ef19298b 编写于 作者: S Stefan Weinhuber 提交者: Martin Schwidefsky

[S390] dasd: add High Performance FICON multitrack support

Some storage systems support multitrack High Performance FICON
requests, which read or write data to more than one track.
This patch enables the DASD device driver to generate multitrack
High Performance FICON requests.
Signed-off-by: NStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: NMartin Schwidefsky <schwidefsky@de.ibm.com>
上级 9062014c
......@@ -745,10 +745,6 @@ struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength,
char *data;
int size;
/* Sanity checks */
BUG_ON(datasize > PAGE_SIZE ||
(cplength*sizeof(struct ccw1)) > PAGE_SIZE);
size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
if (cplength > 0)
size += cplength * sizeof(struct ccw1);
......
......@@ -1105,6 +1105,37 @@ static void dasd_eckd_validate_server(struct dasd_device *device)
"returned rc=%d", private->uid.ssid, rc);
}
static u32 get_fcx_max_data(struct dasd_device *device)
{
#if defined(CONFIG_64BIT)
int tpm, mdc;
int fcx_in_css, fcx_in_gneq, fcx_in_features;
struct dasd_eckd_private *private;
if (dasd_nofcx)
return 0;
/* is transport mode supported? */
private = (struct dasd_eckd_private *) device->private;
fcx_in_css = css_general_characteristics.fcx;
fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
fcx_in_features = private->features.feature[40] & 0x80;
tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
if (!tpm)
return 0;
mdc = ccw_device_get_mdc(device->cdev, 0);
if (mdc < 0) {
dev_warn(&device->cdev->dev, "Detecting the maximum supported"
" data size for zHPF requests failed\n");
return 0;
} else
return mdc * FCX_MAX_DATA_FACTOR;
#else
return 0;
#endif
}
/*
* Check device characteristics.
* If the device is accessible using ECKD discipline, the device is enabled.
......@@ -1223,6 +1254,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
else
private->real_cyl = private->rdc_data.no_cyl;
private->fcx_max_data = get_fcx_max_data(device);
readonly = dasd_device_is_ro(device);
if (readonly)
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
......@@ -2326,6 +2359,12 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
struct tidaw *last_tidaw = NULL;
int itcw_op;
size_t itcw_size;
u8 tidaw_flags;
unsigned int seg_len, part_len, len_to_track_end;
unsigned char new_track;
sector_t recid, trkid;
unsigned int offs;
unsigned int count, count_to_trk_end;
basedev = block->base;
private = (struct dasd_eckd_private *) basedev->private;
......@@ -2341,12 +2380,16 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
/* trackbased I/O needs address all memory via TIDAWs,
* not just for 64 bit addresses. This allows us to map
* each segment directly to one tidaw.
* In the case of write requests, additional tidaws may
* be needed when a segment crosses a track boundary.
*/
trkcount = last_trk - first_trk + 1;
ctidaw = 0;
rq_for_each_segment(bv, req, iter) {
++ctidaw;
}
if (rq_data_dir(req) == WRITE)
ctidaw += (last_trk - first_trk);
/* Allocate the ccw request. */
itcw_size = itcw_calc_size(0, ctidaw, 0);
......@@ -2354,15 +2397,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
if (IS_ERR(cqr))
return cqr;
cqr->cpmode = 1;
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = 100*HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
cqr->retries = 10;
/* transfer length factor: how many bytes to read from the last track */
if (first_trk == last_trk)
tlf = last_offs - first_offs + 1;
......@@ -2371,8 +2405,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
tlf *= blksize;
itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0);
if (IS_ERR(itcw)) {
dasd_sfree_request(cqr, startdev);
return ERR_PTR(-EINVAL);
}
cqr->cpaddr = itcw_get_tcw(itcw);
if (prepare_itcw(itcw, first_trk, last_trk,
cmd, basedev, startdev,
first_offs + 1,
......@@ -2385,26 +2422,64 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
dasd_sfree_request(cqr, startdev);
return ERR_PTR(-EAGAIN);
}
/*
* A tidaw can address 4k of memory, but must not cross page boundaries
* We can let the block layer handle this by setting
* blk_queue_segment_boundary to page boundaries and
* blk_max_segment_size to page size when setting up the request queue.
* For write requests, a TIDAW must not cross track boundaries, because
* we have to set the CBC flag on the last tidaw for each track.
*/
rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset;
last_tidaw = itcw_add_tidaw(itcw, 0x00, dst, bv->bv_len);
if (IS_ERR(last_tidaw))
return (struct dasd_ccw_req *)last_tidaw;
if (rq_data_dir(req) == WRITE) {
new_track = 1;
recid = first_rec;
rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset;
seg_len = bv->bv_len;
while (seg_len) {
if (new_track) {
trkid = recid;
offs = sector_div(trkid, blk_per_trk);
count_to_trk_end = blk_per_trk - offs;
count = min((last_rec - recid + 1),
(sector_t)count_to_trk_end);
len_to_track_end = count * blksize;
recid += count;
new_track = 0;
}
part_len = min(seg_len, len_to_track_end);
seg_len -= part_len;
len_to_track_end -= part_len;
/* We need to end the tidaw at track end */
if (!len_to_track_end) {
new_track = 1;
tidaw_flags = TIDAW_FLAGS_INSERT_CBC;
} else
tidaw_flags = 0;
last_tidaw = itcw_add_tidaw(itcw, tidaw_flags,
dst, part_len);
if (IS_ERR(last_tidaw))
return ERR_PTR(-EINVAL);
dst += part_len;
}
}
} else {
rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset;
last_tidaw = itcw_add_tidaw(itcw, 0x00,
dst, bv->bv_len);
if (IS_ERR(last_tidaw))
return ERR_PTR(-EINVAL);
}
}
last_tidaw->flags |= 0x80;
last_tidaw->flags |= TIDAW_FLAGS_LAST;
last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC;
itcw_finalize(itcw);
if (blk_noretry_request(req) ||
block->base->features & DASD_FEATURE_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->cpmode = 1;
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
......@@ -2420,11 +2495,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
struct dasd_block *block,
struct request *req)
{
int tpm, cmdrtd, cmdwtd;
int cmdrtd, cmdwtd;
int use_prefix;
#if defined(CONFIG_64BIT)
int fcx_in_css, fcx_in_gneq, fcx_in_features;
#endif
int fcx_multitrack;
struct dasd_eckd_private *private;
struct dasd_device *basedev;
sector_t first_rec, last_rec;
......@@ -2432,6 +2505,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
unsigned int first_offs, last_offs;
unsigned int blk_per_trk, blksize;
int cdlspecial;
unsigned int data_size;
struct dasd_ccw_req *cqr;
basedev = block->base;
......@@ -2450,15 +2524,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
last_offs = sector_div(last_trk, blk_per_trk);
cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk);
/* is transport mode supported? */
#if defined(CONFIG_64BIT)
fcx_in_css = css_general_characteristics.fcx;
fcx_in_gneq = private->gneq->reserved2[7] & 0x04;
fcx_in_features = private->features.feature[40] & 0x80;
tpm = fcx_in_css && fcx_in_gneq && fcx_in_features;
#else
tpm = 0;
#endif
fcx_multitrack = private->features.feature[40] & 0x20;
data_size = blk_rq_bytes(req);
/* tpm write request add CBC data on each track boundary */
if (rq_data_dir(req) == WRITE)
data_size += (last_trk - first_trk) * 4;
/* is read track data and write track data in command mode supported? */
cmdrtd = private->features.feature[9] & 0x20;
......@@ -2468,13 +2538,15 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
cqr = NULL;
if (cdlspecial || dasd_page_cache) {
/* do nothing, just fall through to the cmd mode single case */
} else if (!dasd_nofcx && tpm && (first_trk == last_trk)) {
} else if ((data_size <= private->fcx_max_data)
&& (fcx_multitrack || (first_trk == last_trk))) {
cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req,
first_rec, last_rec,
first_trk, last_trk,
first_offs, last_offs,
blk_per_trk, blksize);
if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN)
if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
(PTR_ERR(cqr) != -ENOMEM))
cqr = NULL;
} else if (use_prefix &&
(((rq_data_dir(req) == READ) && cmdrtd) ||
......@@ -2484,7 +2556,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
first_trk, last_trk,
first_offs, last_offs,
blk_per_trk, blksize);
if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN)
if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) &&
(PTR_ERR(cqr) != -ENOMEM))
cqr = NULL;
}
if (!cqr)
......@@ -3279,10 +3352,8 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
{
char *page;
int len, sl, sct, residual;
struct tsb *tsb;
u8 *sense;
u8 *sense, *rcq;
page = (char *) get_zeroed_page(GFP_ATOMIC);
if (page == NULL) {
......@@ -3348,12 +3419,15 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
case 2: /* ts_ddpc */
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" tsb->tsa.ddpc.rcq: ");
for (sl = 0; sl < 16; sl++) {
for (sl = 0; sl < 2; sl++) {
len += sprintf(page + len,
KERN_ERR PRINTK_HEADER
" tsb->tsa.ddpc.rcq %2d-%2d: ",
(8 * sl), ((8 * sl) + 7));
rcq = tsb->tsa.ddpc.rcq;
for (sct = 0; sct < 8; sct++) {
len += sprintf(page + len, " %02x",
tsb->tsa.ddpc.rcq[sl]);
rcq[8 * sl + sct]);
}
len += sprintf(page + len, "\n");
}
......@@ -3573,7 +3647,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
.owner = THIS_MODULE,
.name = "ECKD",
.ebcname = "ECKD",
.max_blocks = 240,
.max_blocks = 190,
.check_device = dasd_eckd_check_characteristics,
.uncheck_device = dasd_eckd_uncheck_device,
.do_analysis = dasd_eckd_do_analysis,
......
......@@ -57,6 +57,10 @@
*/
#define LV_COMPAT_CYL 0xFFFE
#define FCX_MAX_DATA_FACTOR 65536
/*****************************************************************************
* SECTION: Type Definitions
****************************************************************************/
......@@ -455,6 +459,8 @@ struct dasd_eckd_private {
struct alias_pav_group *pavgroup;
struct alias_lcu *lcu;
int count;
u32 fcx_max_data;
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册