]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] s390: dasd driver
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 2 Dec 2004 23:44:48 +0000 (15:44 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 2 Dec 2004 23:44:48 +0000 (15:44 -0800)
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: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/s390/block/dasd.c
drivers/s390/block/dasd_3990_erp.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_diag.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_fba.c
drivers/s390/block/dasd_int.h

index b3714fbd00835f1a3a3293984ed2f5f546e1ba9d..be8ce08e51de2d9cd5836c86598f2f0b268665e4 100644 (file)
@@ -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);
                }
index 680f2e4d933b05c8a44515778ac98811deb0f140..6a39ead68c8fb21c27b32f86c00405a7c7726989 100644 (file)
@@ -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;
                }
index 4751c65e6eba81cdcf015c19dc4071ce7039da20..4a1a4ed6ecae6ac0a0f8068338284d36117a24c0 100644 (file)
@@ -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);
 }
 
index deaecd080203405d281ae83e19b261038e6a29a3..fb81ea4950a45f015d5ba0484d4046387cf93036 100644 (file)
@@ -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,
 };
index 56f471c23a51b199f9862280341edb06b9ffdc32..c2396feb2015f13d5f99aaa06c4fe588ba25c38d 100644 (file)
@@ -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,
 };
index 8116381cf917ee166fbd755083f2122f63a49318..e51bae9cc31e02d1212050aed9f12c3de1ed09ab 100644 (file)
@@ -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,
 };
index 9bdf82fca40c0103c0aa59757aa61d392d064013..7e1f435b59b3df3168d3fbede9f9210df0e2590d 100644 (file)
@@ -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 *