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 @@ ...@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
* *
* $Revision: 1.147 $ * $Revision: 1.151 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -1103,13 +1103,16 @@ static void ...@@ -1103,13 +1103,16 @@ static void
dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data) dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data)
{ {
struct request *req; struct request *req;
struct dasd_device *device;
int status;
req = (struct request *) data; req = (struct request *) data;
dasd_profile_end(cqr->device, cqr, req); device = cqr->device;
spin_lock_irq(&cqr->device->request_queue_lock); dasd_profile_end(device, cqr, req);
dasd_end_request(req, (cqr->status == DASD_CQR_DONE)); status = cqr->device->discipline->free_cp(cqr,req);
spin_unlock_irq(&cqr->device->request_queue_lock); spin_lock_irq(&device->request_queue_lock);
dasd_sfree_request(cqr, cqr->device); 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) ...@@ -1906,8 +1909,10 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
dasd_schedule_bh(device); dasd_schedule_bh(device);
} else { } else {
list_for_each_entry(cqr, &device->ccw_queue, list) 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->status = DASD_CQR_QUEUED;
cqr->retries++;
}
device->stopped |= DASD_STOPPED_DC_WAIT; device->stopped |= DASD_STOPPED_DC_WAIT;
dasd_set_timer(device, 0); dasd_set_timer(device, 0);
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
* *
* $Revision: 1.33 $ * $Revision: 1.34 $
*/ */
#include <linux/timer.h> #include <linux/timer.h>
...@@ -461,6 +461,12 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) ...@@ -461,6 +461,12 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
dasd_3990_erp_block_queue(erp, 30*HZ); 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 { } else {
/* no state change pending - retry */ /* no state change pending - retry */
...@@ -1304,8 +1310,8 @@ dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense) ...@@ -1304,8 +1310,8 @@ dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense)
"fetch mode active"); "fetch mode active");
/* not possible to handle this situation in Linux */ /* not possible to handle this situation in Linux */
panic("No way to inform appliction about the possibly " panic("No way to inform application about the possibly "
"incorret data"); "incorrect data");
} else if (sense[2] & SNS2_ENV_DATA_PRESENT) { } else if (sense[2] & SNS2_ENV_DATA_PRESENT) {
...@@ -2203,6 +2209,13 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) ...@@ -2203,6 +2209,13 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
erp = dasd_3990_erp_action_4(erp, sense); erp = dasd_3990_erp_action_4(erp, sense);
break; 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 */ default: /* all others errors - default erp */
break; break;
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* functions may not be called from interrupt context. In particular * functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context. * dasd_get_device is a no-no from interrupt context.
* *
* $Revision: 1.33 $ * $Revision: 1.34 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
#include "dasd_int.h" #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 * dasd_devmap_t is used to store the features and the relation
* between device number and device index. To find a dasd_devmap_t * between device number and device index. To find a dasd_devmap_t
...@@ -235,6 +238,20 @@ dasd_parse_keyword( char *parsestring ) { ...@@ -235,6 +238,20 @@ dasd_parse_keyword( char *parsestring ) {
"turning to probeonly mode"); "turning to probeonly mode");
return residual_str; 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); return ERR_PTR(-EINVAL);
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.38 $ * $Revision: 1.39 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -413,6 +413,16 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -413,6 +413,16 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
return cqr; 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 static int
dasd_diag_fill_info(struct dasd_device * device, dasd_diag_fill_info(struct dasd_device * device,
struct dasd_information2_t * info) struct dasd_information2_t * info)
...@@ -475,6 +485,7 @@ struct dasd_discipline dasd_diag_discipline = { ...@@ -475,6 +485,7 @@ struct dasd_discipline dasd_diag_discipline = {
.erp_action = dasd_diag_erp_action, .erp_action = dasd_diag_erp_action,
.erp_postaction = dasd_diag_erp_postaction, .erp_postaction = dasd_diag_erp_postaction,
.build_cp = dasd_diag_build_cp, .build_cp = dasd_diag_build_cp,
.free_cp = dasd_diag_free_cp,
.dump_sense = dasd_diag_dump_sense, .dump_sense = dasd_diag_dump_sense,
.fill_info = dasd_diag_fill_info, .fill_info = dasd_diag_fill_info,
}; };
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.61 $ * $Revision: 1.65 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -1035,6 +1035,14 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -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) { rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
dst = page_address(bv->bv_page) + bv->bv_offset; 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) { for (off = 0; off < bv->bv_len; off += blksize) {
sector_t trkid = recid; sector_t trkid = recid;
unsigned int recoffs = sector_div(trkid, blk_per_trk); unsigned int recoffs = sector_div(trkid, blk_per_trk);
...@@ -1088,6 +1096,58 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) ...@@ -1088,6 +1096,58 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
return cqr; 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 static int
dasd_eckd_fill_info(struct dasd_device * device, dasd_eckd_fill_info(struct dasd_device * device,
struct dasd_information2_t * info) struct dasd_information2_t * info)
...@@ -1474,6 +1534,7 @@ static struct dasd_discipline dasd_eckd_discipline = { ...@@ -1474,6 +1534,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
.erp_action = dasd_eckd_erp_action, .erp_action = dasd_eckd_erp_action,
.erp_postaction = dasd_eckd_erp_postaction, .erp_postaction = dasd_eckd_erp_postaction,
.build_cp = dasd_eckd_build_cp, .build_cp = dasd_eckd_build_cp,
.free_cp = dasd_eckd_free_cp,
.dump_sense = dasd_eckd_dump_sense, .dump_sense = dasd_eckd_dump_sense,
.fill_info = dasd_eckd_fill_info, .fill_info = dasd_eckd_fill_info,
}; };
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.34 $ * $Revision: 1.37 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -311,6 +311,14 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -311,6 +311,14 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
recid = first_rec; recid = first_rec;
rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) { rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
dst = page_address(bv->bv_page) + bv->bv_offset; 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) { for (off = 0; off < bv->bv_len; off += blksize) {
/* Locate record for stupid devices. */ /* Locate record for stupid devices. */
if (private->rdc_data.mode.bits.data_chain == 0) { if (private->rdc_data.mode.bits.data_chain == 0) {
...@@ -347,6 +355,54 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req) ...@@ -347,6 +355,54 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
return cqr; 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 static int
dasd_fba_fill_info(struct dasd_device * device, dasd_fba_fill_info(struct dasd_device * device,
struct dasd_information2_t * info) struct dasd_information2_t * info)
...@@ -410,6 +466,7 @@ static struct dasd_discipline dasd_fba_discipline = { ...@@ -410,6 +466,7 @@ static struct dasd_discipline dasd_fba_discipline = {
.erp_action = dasd_fba_erp_action, .erp_action = dasd_fba_erp_action,
.erp_postaction = dasd_fba_erp_postaction, .erp_postaction = dasd_fba_erp_postaction,
.build_cp = dasd_fba_build_cp, .build_cp = dasd_fba_build_cp,
.free_cp = dasd_fba_free_cp,
.dump_sense = dasd_fba_dump_sense, .dump_sense = dasd_fba_dump_sense,
.fill_info = dasd_fba_fill_info, .fill_info = dasd_fba_fill_info,
}; };
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.60 $ * $Revision: 1.61 $
*/ */
#ifndef DASD_INT_H #ifndef DASD_INT_H
...@@ -240,7 +240,7 @@ struct dasd_discipline { ...@@ -240,7 +240,7 @@ struct dasd_discipline {
int (*term_IO) (struct dasd_ccw_req *); int (*term_IO) (struct dasd_ccw_req *);
struct dasd_ccw_req *(*format_device) (struct dasd_device *, struct dasd_ccw_req *(*format_device) (struct dasd_device *,
struct format_data_t *); struct format_data_t *);
int (*free_cp) (struct dasd_ccw_req *, struct request *);
/* /*
* Error recovery functions. examine_error() returns a value that * Error recovery functions. examine_error() returns a value that
* indicates what to do for an error condition. If examine_error() * indicates what to do for an error condition. If examine_error()
...@@ -438,6 +438,8 @@ extern struct dasd_profile_info_t dasd_global_profile; ...@@ -438,6 +438,8 @@ extern struct dasd_profile_info_t dasd_global_profile;
extern unsigned int dasd_profile_level; extern unsigned int dasd_profile_level;
extern struct block_device_operations dasd_device_operations; extern struct block_device_operations dasd_device_operations;
extern kmem_cache_t *dasd_page_cache;
struct dasd_ccw_req * struct dasd_ccw_req *
dasd_kmalloc_request(char *, int, int, struct dasd_device *); dasd_kmalloc_request(char *, int, int, struct dasd_device *);
struct dasd_ccw_req * 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