This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / s390 / block / dasd_eckd.c
index 27bf674..cfd5884 100644 (file)
@@ -7,7 +7,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.57 $
+ * $Revision: 1.63 $
  */
 
 #include <linux/config.h>
@@ -68,6 +68,10 @@ static struct ccw_device_id dasd_eckd_ids[] = {
        { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4},
        { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5},
        { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6},
+       { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), driver_info: 0x7},
+       { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), driver_info: 0x8},
+       { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), driver_info: 0x9},
+       { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), driver_info: 0xa},
        { /* end of list */ },
 };
 
@@ -274,9 +278,11 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
 
        data->attributes.mode = 0x3;    /* ECKD */
 
-       if (private->rdc_data.cu_type == 0x2105
+       if ((private->rdc_data.cu_type == 0x2105 ||
+            private->rdc_data.cu_type == 0x2107 ||
+            private->rdc_data.cu_type == 0x1750)
            && !(private->uses_cdl && trk < 2))
-               data->ga_extended |= 0x40;
+               data->ga_extended |= 0x40; /* Regular Data Format Mode */
 
        geo.cyl = private->rdc_data.no_cyl;
        geo.head = private->rdc_data.trk_per_cyl;
@@ -499,11 +505,13 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                        "sizes of configuration data mismatch"
                        "%d (read) vs %ld (expected)",
                        conf_len, sizeof (struct dasd_eckd_confdata));
+
+               kfree(conf_data); /* allocated by read_conf_data() */
                return 0;       /* no errror */
        }
        memcpy(&private->conf_data, conf_data,
               sizeof (struct dasd_eckd_confdata));
-
+       kfree(conf_data); /* allocated by read_conf_data() */
        return 0;
 }
 
@@ -903,6 +911,8 @@ dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
        switch (cdev->id.cu_type) {
        case 0x3990:
        case 0x2105:
+       case 0x2107:
+       case 0x1750:
                return dasd_3990_erp_examine(cqr, irb);
        case 0x9343:
                return dasd_9343_erp_examine(cqr, irb);
@@ -923,6 +933,8 @@ dasd_eckd_erp_action(struct dasd_ccw_req * cqr)
        switch (cdev->id.cu_type) {
        case 0x3990:
        case 0x2105:
+       case 0x2107:
+       case 0x1750:
                return dasd_3990_erp_action;
        case 0x9343:
                /* Return dasd_9343_erp_action; */
@@ -1023,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_ATOMIC | 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);
@@ -1076,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)
@@ -1462,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,
 };