This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / s390 / block / dasd_eckd.c
index 56f471c..cfd5884 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.63 $
  */
 
 #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_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);
@@ -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,
 };