diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index b3714fbd00835f1a3a3293984ed2f5f546e1ba9d..be8ce08e51de2d9cd5836c86598f2f0b268665e4 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -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); } diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 680f2e4d933b05c8a44515778ac98811deb0f140..6a39ead68c8fb21c27b32f86c00405a7c7726989 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -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; } diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 4751c65e6eba81cdcf015c19dc4071ce7039da20..4a1a4ed6ecae6ac0a0f8068338284d36117a24c0 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -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); } diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index deaecd080203405d281ae83e19b261038e6a29a3..fb81ea4950a45f015d5ba0484d4046387cf93036 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -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, }; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 56f471c23a51b199f9862280341edb06b9ffdc32..c2396feb2015f13d5f99aaa06c4fe588ba25c38d 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -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, }; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 8116381cf917ee166fbd755083f2122f63a49318..e51bae9cc31e02d1212050aed9f12c3de1ed09ab 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -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, }; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 9bdf82fca40c0103c0aa59757aa61d392d064013..7e1f435b59b3df3168d3fbede9f9210df0e2590d 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -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 *