vserver 1.9.5.x5
[linux-2.6.git] / drivers / s390 / block / dasd_eckd.c
index 56f471c..df89023 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.68 $
  */
 
 #include <linux/config.h>
 
 #define ECKD_C0(i) (i->home_bytes)
 #define ECKD_F(i) (i->formula)
-#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):(i->factors.f_0x02.f1))
-#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):(i->factors.f_0x02.f2))
-#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):(i->factors.f_0x02.f3))
+#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):\
+                   (i->factors.f_0x02.f1))
+#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):\
+                   (i->factors.f_0x02.f2))
+#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):\
+                   (i->factors.f_0x02.f3))
 #define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0)
 #define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0)
 #define ECKD_F6(i) (i->factor6)
@@ -151,7 +154,6 @@ bytes_per_record(struct dasd_eckd_characteristics *rdc, int kl, int dl)
                bpr = fl1 + fl2;
                break;
        default:
-               MESSAGE(KERN_ERR, "unknown formula%d", rdc->formula);
                bpr = 0;
                break;
        }
@@ -209,8 +211,8 @@ check_XRC (struct ccw1         *de_ccw,
         /* switch on System Time Stamp - needed for XRC Support */
         if (private->rdc_data.facilities.XRC_supported) {
                 
-                data->ga_extended |= 0x08;   /* switch on 'Time Stamp Valid'   */
-                data->ga_extended |= 0x02;   /* switch on 'Extended Parameter' */
+                data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */
+                data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
                 
                 data->ep_sys_time = get_clock ();
                 
@@ -272,7 +274,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
                 check_XRC (ccw, data, device);
                break;
        default:
-               MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd);
+               DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
                break;
        }
 
@@ -318,7 +320,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
                                
        private = (struct dasd_eckd_private *) device->private;
 
-       DBF_EVENT(DBF_INFO,
+       DBF_DEV_EVENT(DBF_INFO, device,
                  "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d",
                  trk, rec_on_trk, no_rec, cmd, reclen);
 
@@ -400,7 +402,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
                data->operation.operation = 0x0b;
                break;
        default:
-               MESSAGE(KERN_ERR, "unknown opcode 0x%x", cmd);
+               DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
        }
        data->seek_addr.cyl = data->search_arg.cyl =
                trk / private->rdc_data.trk_per_cyl;
@@ -458,8 +460,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                private = kmalloc(sizeof(struct dasd_eckd_private),
                                  GFP_KERNEL | GFP_DMA);
                if (private == NULL) {
-                       MESSAGE(KERN_WARNING, "%s",
-                               "memory allocation failed for private data");
+                       DEV_MESSAGE(KERN_WARNING, device, "%s",
+                                   "memory allocation failed for private "
+                                   "data");
                        return -ENOMEM;
                }
                device->private = (void *) private;
@@ -474,8 +477,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        rdc_data = (void *) &(private->rdc_data);
        rc = read_dev_chars(device->cdev, &rdc_data, 64);
        if (rc) {
-               MESSAGE(KERN_WARNING,
-                       "Read device characteristics returned error %d", rc);
+               DEV_MESSAGE(KERN_WARNING, device,
+                           "Read device characteristics returned error %d",
+                           rc);
                return rc;
        }
 
@@ -492,19 +496,20 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        /* Read Configuration Data */
        rc = read_conf_data(device->cdev, &conf_data, &conf_len);
        if (rc && rc != -EOPNOTSUPP) {  /* -EOPNOTSUPP is ok */
-               MESSAGE(KERN_WARNING,
-                       "Read configuration data returned error %d", rc);
+               DEV_MESSAGE(KERN_WARNING, device,
+                           "Read configuration data returned error %d", rc);
                return rc;
        }
        if (conf_data == NULL) {
-               MESSAGE(KERN_WARNING, "%s", "No configuration data retrieved");
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "No configuration data retrieved");
                return 0;       /* no errror */
        }
        if (conf_len != sizeof (struct dasd_eckd_confdata)) {
-               MESSAGE(KERN_WARNING,
-                       "sizes of configuration data mismatch"
-                       "%d (read) vs %ld (expected)",
-                       conf_len, sizeof (struct dasd_eckd_confdata));
+               DEV_MESSAGE(KERN_WARNING, 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 */
@@ -746,8 +751,9 @@ dasd_eckd_format_device(struct dasd_device * device,
                return ERR_PTR(-EINVAL);
        }
        if (dasd_check_blocksize(fdata->blksize) != 0) {
-               MESSAGE(KERN_WARNING, "Invalid blocksize %d...terminating!",
-                       fdata->blksize);
+               DEV_MESSAGE(KERN_WARNING, device,
+                           "Invalid blocksize %d...terminating!",
+                           fdata->blksize);
                return ERR_PTR(-EINVAL);
        }
 
@@ -783,7 +789,8 @@ dasd_eckd_format_device(struct dasd_device * device,
                        sizeof(struct eckd_count);
                break;
        default:
-               MESSAGE(KERN_WARNING, "Invalid flags 0x%x.", fdata->intensity);
+               DEV_MESSAGE(KERN_WARNING, device, "Invalid flags 0x%x.",
+                           fdata->intensity);
                return ERR_PTR(-EINVAL);
        }
        /* Allocate the format ccw request. */
@@ -918,8 +925,8 @@ dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
                return dasd_9343_erp_examine(cqr, irb);
        case 0x3880:
        default:
-               MESSAGE(KERN_WARNING, "%s",
-                       "default (unknown CU type) - RECOVERABLE return");
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "default (unknown CU type) - RECOVERABLE return");
                return dasd_era_recover;
        }
 }
@@ -937,7 +944,6 @@ dasd_eckd_erp_action(struct dasd_ccw_req * cqr)
        case 0x1750:
                return dasd_3990_erp_action;
        case 0x9343:
-               /* Return dasd_9343_erp_action; */
        case 0x3880:
        default:
                return dasd_default_erp_action;
@@ -995,7 +1001,8 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
                                return ERR_PTR(-EINVAL);
                        count += bv->bv_len >> (device->s2b_shift + 9);
 #if defined(CONFIG_ARCH_S390X)
-                       if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
+                       if (idal_is_needed (page_address(bv->bv_page),
+                                           bv->bv_len))
                                cidaw += bv->bv_len >> (device->s2b_shift + 9);
 #endif
                }
@@ -1035,6 +1042,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 +1103,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)
@@ -1132,10 +1199,10 @@ dasd_eckd_release(struct block_device *bdev, int no, long args)
 
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1, 32, device);
-       if (cqr == NULL) {
-               MESSAGE(KERN_WARNING, "%s",
-                       "No memory to allocate initialization request");
-               return -ENOMEM;
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "Could not allocate initialization request");
+               return PTR_ERR(cqr);
        }
        cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE;
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
@@ -1176,10 +1243,10 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
 
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1, 32, device);
-       if (cqr == NULL) {
-               MESSAGE(KERN_WARNING, "%s",
-                       "No memory to allocate initialization request");
-               return -ENOMEM;
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "Could not allocate initialization request");
+               return PTR_ERR(cqr);
        }
        cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE;
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
@@ -1219,10 +1286,10 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
 
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1, 32, device);
-       if (cqr == NULL) {
-               MESSAGE(KERN_WARNING, "%s",
-                       "No memory to allocate initialization request");
-               return -ENOMEM;
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "Could not allocate initialization request");
+               return PTR_ERR(cqr);
        }
        cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK;
         cqr->cpaddr->flags |= CCW_FLAG_SLI;
@@ -1263,10 +1330,10 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
                                   (sizeof (struct dasd_psf_prssd_data) +
                                    sizeof (struct dasd_rssd_perf_stats_t)),
                                   device);
-       if (cqr == NULL) {
-               MESSAGE(KERN_WARNING, "%s",
-                       "No memory to allocate initialization request");
-               return -ENOMEM;
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "Could not allocate initialization request");
+               return PTR_ERR(cqr);
        }
        cqr->device = device;
        cqr->retries = 0;
@@ -1362,58 +1429,47 @@ dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
                           sizeof (struct attrib_data_t))) {
                return -EFAULT;
        }
-
        private = (struct dasd_eckd_private *) device->private;
-
        private->attrib = attrib;
 
-       DBF_DEV_EVENT(DBF_ERR, device,
-                     "cache operation mode set to "
-                     "%x (%i cylinder prestage)",
-                     private->attrib.operation, private->attrib.nr_cyl);
-       
+       DEV_MESSAGE(KERN_INFO, device,
+                   "cache operation mode set to %x (%i cylinder prestage)",
+                   private->attrib.operation, private->attrib.nr_cyl);
        return 0;
 }
 
+/*
+ * Print sense data and related channel program.
+ * Parts are printed because printk buffer is only 1024 bytes.
+ */
 static void
 dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
                     struct irb *irb)
 {
-
        char *page;
-       struct ccw1 *act;
-       int len, sl, sct;
+       struct ccw1 *act, *end, *last;
+       int len, sl, sct, count;
 
        page = (char *) get_zeroed_page(GFP_ATOMIC);
        if (page == NULL) {
-               MESSAGE(KERN_ERR, "%s", "No memory to dump sense data");
+               DEV_MESSAGE(KERN_ERR, device, " %s",
+                           "No memory to dump sense data");
                return;
        }
        len = sprintf(page, KERN_ERR PRINTK_HEADER
-                     "device %s: I/O status report:\n",
+                     " I/O status report for device %s:\n",
                      device->cdev->dev.bus_id);
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                      "in req: %p CS: 0x%02X DS: 0x%02X\n", req,
+                      " in req: %p CS: 0x%02X DS: 0x%02X\n", req,
                       irb->scsw.cstat, irb->scsw.dstat);
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                      "Failing CCW: %p\n", 
+                      " device %s: Failing CCW: %p\n",
+                      device->cdev->dev.bus_id,
                       (void *) (addr_t) irb->scsw.cpa);
-       act = req->cpaddr;
-       do {
-               DBF_EVENT(DBF_INFO,
-                         "CCW %p: %08X %08X",
-                         act, ((int *) act)[0], ((int *) act)[1]);
-               DBF_EVENT(DBF_INFO,
-                         "DAT: %08X %08X %08X %08X",
-                         ((int *) (addr_t) act->cda)[0],
-                         ((int *) (addr_t) act->cda)[1],
-                         ((int *) (addr_t) act->cda)[2],
-                         ((int *) (addr_t) act->cda)[3]);
-       } while (act++->flags & (CCW_FLAG_CC | CCW_FLAG_DC));
        if (irb->esw.esw0.erw.cons) {
                for (sl = 0; sl < 4; sl++) {
                        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                                      "Sense(hex) %2d-%2d:",
+                                      " Sense(hex) %2d-%2d:",
                                       (8 * sl), ((8 * sl) + 7));
 
                        for (sct = 0; sct < 8; sct++) {
@@ -1426,23 +1482,86 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
                if (irb->ecw[27] & DASD_SENSE_BIT_0) {
                        /* 24 Byte Sense Data */
                        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                                      "24 Byte: %x MSG %x, %s MSGb to SYSOP\n",
+                                      " 24 Byte: %x MSG %x, "
+                                      "%s MSGb to SYSOP\n",
                                       irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
                                       irb->ecw[1] & 0x10 ? "" : "no");
                } else {
                        /* 32 Byte Sense Data */
                        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                                      "32 Byte: Format: %x "
+                                      " 32 Byte: Format: %x "
                                       "Exception class %x\n",
                                       irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
                }
        } else {
                len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                              "SORRY - NO VALID SENSE AVAILABLE\n");
+                              " SORRY - NO VALID SENSE AVAILABLE\n");
        }
+       MESSAGE_LOG(KERN_ERR, "%s",
+                   page + sizeof(KERN_ERR PRINTK_HEADER));
 
-       MESSAGE(KERN_ERR, "Sense data:\n%s", page);
+       /* dump the Channel Program */
+       /* print first CCWs (maximum 8) */
+       act = req->cpaddr;
+        for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+       end = min(act + 8, last);
+       len = sprintf(page, KERN_ERR PRINTK_HEADER
+                     " Related CP in req: %p\n", req);
+       while (act <= end) {
+               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+                              " CCW %p: %08X %08X DAT:",
+                              act, ((int *) act)[0], ((int *) act)[1]);
+               for (count = 0; count < 32 && count < act->count;
+                    count += sizeof(int))
+                       len += sprintf(page + len, " %08X",
+                                      ((int *) (addr_t) act->cda)
+                                      [(count>>2)]);
+               len += sprintf(page + len, "\n");
+               act++;
+       }
+       MESSAGE_LOG(KERN_ERR, "%s",
+                   page + sizeof(KERN_ERR PRINTK_HEADER));
+
+       /* print failing CCW area */
+       len = 0;
+       if (act <  ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
+               act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
+               len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+       }
+       end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
+       while (act <= end) {
+               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+                              " CCW %p: %08X %08X DAT:",
+                              act, ((int *) act)[0], ((int *) act)[1]);
+               for (count = 0; count < 32 && count < act->count;
+                    count += sizeof(int))
+                       len += sprintf(page + len, " %08X",
+                                      ((int *) (addr_t) act->cda)
+                                      [(count>>2)]);
+               len += sprintf(page + len, "\n");
+               act++;
+       }
 
+       /* print last CCWs */
+       if (act <  last - 2) {
+               act = last - 2;
+               len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+       }
+       while (act <= last) {
+               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+                              " CCW %p: %08X %08X DAT:",
+                              act, ((int *) act)[0], ((int *) act)[1]);
+               for (count = 0; count < 32 && count < act->count;
+                    count += sizeof(int))
+                       len += sprintf(page + len, " %08X",
+                                      ((int *) (addr_t) act->cda)
+                                      [(count>>2)]);
+               len += sprintf(page + len, "\n");
+               act++;
+       }
+       if (len > 0)
+               MESSAGE_LOG(KERN_ERR, "%s",
+                           page + sizeof(KERN_ERR PRINTK_HEADER));
        free_page((unsigned long) page);
 }
 
@@ -1474,6 +1593,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,
 };