Commit 71dff474 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: dasd driver

From: Horst Hummel <horst.hummel@de.ibm.com>
From: Stefan Weinhuber <wein@de.ibm.com>

dasd driver changes:
 - Introduce "fixbuffers" dasd option that uses buffer pages for
   the dasd i/o similar to bounce buffers.
 - Fix I/O errors when using XRC.
 - Increment retry counter again if notifier callback is called with CIO_GONE.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b7f64368
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
* $Revision: 1.147 $
* $Revision: 1.151 $
*/
#include <linux/config.h>
......@@ -1103,13 +1103,16 @@ static void
dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data)
{
struct request *req;
struct dasd_device *device;
int status;
req = (struct request *) data;
dasd_profile_end(cqr->device, cqr, req);
spin_lock_irq(&cqr->device->request_queue_lock);
dasd_end_request(req, (cqr->status == DASD_CQR_DONE));
spin_unlock_irq(&cqr->device->request_queue_lock);
dasd_sfree_request(cqr, cqr->device);
device = cqr->device;
dasd_profile_end(device, cqr, req);
status = cqr->device->discipline->free_cp(cqr,req);
spin_lock_irq(&device->request_queue_lock);
dasd_end_request(req, status);
spin_unlock_irq(&device->request_queue_lock);
}
......@@ -1906,8 +1909,10 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
dasd_schedule_bh(device);
} else {
list_for_each_entry(cqr, &device->ccw_queue, list)
if (cqr->status == DASD_CQR_IN_IO)
if (cqr->status == DASD_CQR_IN_IO) {
cqr->status = DASD_CQR_QUEUED;
cqr->retries++;
}
device->stopped |= DASD_STOPPED_DC_WAIT;
dasd_set_timer(device, 0);
}
......
......@@ -5,7 +5,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
*
* $Revision: 1.33 $
* $Revision: 1.34 $
*/
#include <linux/timer.h>
......@@ -461,6 +461,12 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
dasd_3990_erp_block_queue(erp, 30*HZ);
} else if (sense[25] == 0x1E) { /* busy */
DEV_MESSAGE(KERN_INFO, device,
"busy - redriving request later, "
"%d retries left",
erp->retries);
dasd_3990_erp_block_queue(erp, HZ);
} else {
/* no state change pending - retry */
......@@ -1304,8 +1310,8 @@ dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense)
"fetch mode active");
/* not possible to handle this situation in Linux */
panic("No way to inform appliction about the possibly "
"incorret data");
panic("No way to inform application about the possibly "
"incorrect data");
} else if (sense[2] & SNS2_ENV_DATA_PRESENT) {
......@@ -2203,6 +2209,13 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
erp = dasd_3990_erp_action_4(erp, sense);
break;
case 0x1E: /* busy */
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Busy condition exists "
"for the subsystem or device");
erp = dasd_3990_erp_action_4(erp, sense);
break;
default: /* all others errors - default erp */
break;
}
......
......@@ -11,7 +11,7 @@
* functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context.
*
* $Revision: 1.33 $
* $Revision: 1.34 $
*/
#include <linux/config.h>
......@@ -26,6 +26,9 @@
#include "dasd_int.h"
kmem_cache_t *dasd_page_cache;
EXPORT_SYMBOL(dasd_page_cache);
/*
* dasd_devmap_t is used to store the features and the relation
* between device number and device index. To find a dasd_devmap_t
......@@ -235,6 +238,20 @@ dasd_parse_keyword( char *parsestring ) {
"turning to probeonly mode");
return residual_str;
}
if (strncmp ("fixedbuffers", parsestring, length) == 0) {
if (dasd_page_cache)
return residual_str;
dasd_page_cache =
kmem_cache_create("dasd_page_cache", PAGE_SIZE, 0,
SLAB_CACHE_DMA, NULL, NULL );
if (!dasd_page_cache)
MESSAGE(KERN_WARNING, "%s", "Failed to create slab, "
"fixed buffer mode disabled.");
else
MESSAGE (KERN_INFO, "%s",
"turning on fixed buffer mode");
return residual_str;
}
return ERR_PTR(-EINVAL);
}
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.38 $
* $Revision: 1.39 $
*/
#include <linux/config.h>
......@@ -413,6 +413,16 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
return cqr;
}
static int
dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
{
int status;
status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device);
return status;
}
static int
dasd_diag_fill_info(struct dasd_device * device,
struct dasd_information2_t * info)
......@@ -475,6 +485,7 @@ struct dasd_discipline dasd_diag_discipline = {
.erp_action = dasd_diag_erp_action,
.erp_postaction = dasd_diag_erp_postaction,
.build_cp = dasd_diag_build_cp,
.free_cp = dasd_diag_free_cp,
.dump_sense = dasd_diag_dump_sense,
.fill_info = dasd_diag_fill_info,
};
......
......@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.61 $
* $Revision: 1.65 $
*/
#include <linux/config.h>
......@@ -1035,6 +1035,14 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
}
rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
dst = page_address(bv->bv_page) + bv->bv_offset;
if (dasd_page_cache) {
char *copy = kmem_cache_alloc(dasd_page_cache,
SLAB_DMA | __GFP_NOWARN);
if (copy && rq_data_dir(req) == WRITE)
memcpy(copy + bv->bv_offset, dst, bv->bv_len);
if (copy)
dst = copy + bv->bv_offset;
}
for (off = 0; off < bv->bv_len; off += blksize) {
sector_t trkid = recid;
unsigned int recoffs = sector_div(trkid, blk_per_trk);
......@@ -1088,6 +1096,58 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
return cqr;
}
static int
dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
{
struct dasd_eckd_private *private;
struct ccw1 *ccw;
struct bio *bio;
struct bio_vec *bv;
char *dst, *cda;
unsigned int blksize, blk_per_trk, off;
sector_t recid;
int i, status;
if (!dasd_page_cache)
goto out;
private = (struct dasd_eckd_private *) cqr->device->private;
blksize = cqr->device->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
recid = req->sector >> cqr->device->s2b_shift;
ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */
ccw++;
if (private->uses_cdl == 0 || recid > 2*blk_per_trk)
ccw++;
rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
dst = page_address(bv->bv_page) + bv->bv_offset;
for (off = 0; off < bv->bv_len; off += blksize) {
/* Skip locate record. */
if (private->uses_cdl && recid <= 2*blk_per_trk)
ccw++;
if (dst) {
if (ccw->flags & CCW_FLAG_IDA)
cda = *((char **)((addr_t) ccw->cda));
else
cda = (char *)((addr_t) ccw->cda);
if (dst != cda) {
if (rq_data_dir(req) == READ)
memcpy(dst, cda, bv->bv_len);
kmem_cache_free(dasd_page_cache,
(void *)((addr_t)cda & PAGE_MASK));
}
dst = NULL;
}
ccw++;
recid++;
}
}
out:
status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device);
return status;
}
static int
dasd_eckd_fill_info(struct dasd_device * device,
struct dasd_information2_t * info)
......@@ -1474,6 +1534,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
.erp_action = dasd_eckd_erp_action,
.erp_postaction = dasd_eckd_erp_postaction,
.build_cp = dasd_eckd_build_cp,
.free_cp = dasd_eckd_free_cp,
.dump_sense = dasd_eckd_dump_sense,
.fill_info = dasd_eckd_fill_info,
};
......
......@@ -4,7 +4,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.34 $
* $Revision: 1.37 $
*/
#include <linux/config.h>
......@@ -311,6 +311,14 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
recid = first_rec;
rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
dst = page_address(bv->bv_page) + bv->bv_offset;
if (dasd_page_cache) {
char *copy = kmem_cache_alloc(dasd_page_cache,
SLAB_DMA | __GFP_NOWARN);
if (copy && rq_data_dir(req) == WRITE)
memcpy(copy + bv->bv_offset, dst, bv->bv_len);
if (copy)
dst = copy + bv->bv_offset;
}
for (off = 0; off < bv->bv_len; off += blksize) {
/* Locate record for stupid devices. */
if (private->rdc_data.mode.bits.data_chain == 0) {
......@@ -347,6 +355,54 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
return cqr;
}
static int
dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
{
struct dasd_fba_private *private;
struct ccw1 *ccw;
struct bio *bio;
struct bio_vec *bv;
char *dst, *cda;
unsigned int blksize, off;
int i, status;
if (!dasd_page_cache)
goto out;
private = (struct dasd_fba_private *) cqr->device->private;
blksize = cqr->device->bp_block;
ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */
ccw++;
if (private->rdc_data.mode.bits.data_chain != 0)
ccw++;
rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
dst = page_address(bv->bv_page) + bv->bv_offset;
for (off = 0; off < bv->bv_len; off += blksize) {
/* Skip locate record. */
if (private->rdc_data.mode.bits.data_chain == 0)
ccw++;
if (dst) {
if (ccw->flags & CCW_FLAG_IDA)
cda = *((char **)((addr_t) ccw->cda));
else
cda = (char *)((addr_t) ccw->cda);
if (dst != cda) {
if (rq_data_dir(req) == READ)
memcpy(dst, cda, bv->bv_len);
kmem_cache_free(dasd_page_cache,
(void *)((addr_t)cda & PAGE_MASK));
}
dst = NULL;
}
ccw++;
}
}
out:
status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device);
return status;
}
static int
dasd_fba_fill_info(struct dasd_device * device,
struct dasd_information2_t * info)
......@@ -410,6 +466,7 @@ static struct dasd_discipline dasd_fba_discipline = {
.erp_action = dasd_fba_erp_action,
.erp_postaction = dasd_fba_erp_postaction,
.build_cp = dasd_fba_build_cp,
.free_cp = dasd_fba_free_cp,
.dump_sense = dasd_fba_dump_sense,
.fill_info = dasd_fba_fill_info,
};
......
......@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* $Revision: 1.60 $
* $Revision: 1.61 $
*/
#ifndef DASD_INT_H
......@@ -240,7 +240,7 @@ struct dasd_discipline {
int (*term_IO) (struct dasd_ccw_req *);
struct dasd_ccw_req *(*format_device) (struct dasd_device *,
struct format_data_t *);
int (*free_cp) (struct dasd_ccw_req *, struct request *);
/*
* Error recovery functions. examine_error() returns a value that
* indicates what to do for an error condition. If examine_error()
......@@ -438,6 +438,8 @@ extern struct dasd_profile_info_t dasd_global_profile;
extern unsigned int dasd_profile_level;
extern struct block_device_operations dasd_device_operations;
extern kmem_cache_t *dasd_page_cache;
struct dasd_ccw_req *
dasd_kmalloc_request(char *, int, int, struct dasd_device *);
struct dasd_ccw_req *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment