]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/s390/block/dasd_eckd.c
[S390] dasd_eer: use mutex instead of semaphore
[linux-2.6-omap-h63xx.git] / drivers / s390 / block / dasd_eckd.c
index fdaa471e845fa78c73ff7a704bada1fb0f0b3ff7..418b4e63a4fab42473f9d650073171a81543a8b4 100644 (file)
@@ -134,44 +134,7 @@ ceil_quot(unsigned int d1, unsigned int d2)
        return (d1 + (d2 - 1)) / d2;
 }
 
-static inline int
-bytes_per_record(struct dasd_eckd_characteristics *rdc, int kl, int dl)
-{
-       unsigned int fl1, fl2, int1, int2;
-       int bpr;
-
-       switch (rdc->formula) {
-       case 0x01:
-               fl1 = round_up_multiple(ECKD_F2(rdc) + dl, ECKD_F1(rdc));
-               fl2 = round_up_multiple(kl ? ECKD_F2(rdc) + kl : 0,
-                                       ECKD_F1(rdc));
-               bpr = fl1 + fl2;
-               break;
-       case 0x02:
-               int1 = ceil_quot(dl + ECKD_F6(rdc), ECKD_F5(rdc) << 1);
-               int2 = ceil_quot(kl + ECKD_F6(rdc), ECKD_F5(rdc) << 1);
-               fl1 = round_up_multiple(ECKD_F1(rdc) * ECKD_F2(rdc) + dl +
-                                       ECKD_F6(rdc) + ECKD_F4(rdc) * int1,
-                                       ECKD_F1(rdc));
-               fl2 = round_up_multiple(ECKD_F1(rdc) * ECKD_F3(rdc) + kl +
-                                       ECKD_F6(rdc) + ECKD_F4(rdc) * int2,
-                                       ECKD_F1(rdc));
-               bpr = fl1 + fl2;
-               break;
-       default:
-               bpr = 0;
-               break;
-       }
-       return bpr;
-}
-
-static inline unsigned int
-bytes_per_track(struct dasd_eckd_characteristics *rdc)
-{
-       return *(unsigned int *) (rdc->byte_per_track) >> 8;
-}
-
-static inline unsigned int
+static unsigned int
 recs_per_track(struct dasd_eckd_characteristics * rdc,
               unsigned int kl, unsigned int dl)
 {
@@ -204,37 +167,39 @@ recs_per_track(struct dasd_eckd_characteristics * rdc,
        return 0;
 }
 
-static inline void
+static int
 check_XRC (struct ccw1         *de_ccw,
            struct DE_eckd_data *data,
            struct dasd_device  *device)
 {
         struct dasd_eckd_private *private;
+       int rc;
 
         private = (struct dasd_eckd_private *) device->private;
+       if (!private->rdc_data.facilities.XRC_supported)
+               return 0;
 
         /* 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 ();
+       rc = get_sync_clock(&data->ep_sys_time);
+       /* Ignore return code if sync clock is switched off. */
+       if (rc == -ENOSYS || rc == -EACCES)
+               rc = 0;
 
-                de_ccw->count = sizeof (struct DE_eckd_data);
-               de_ccw->flags |= CCW_FLAG_SLI;
-        }
-
-        return;
-
-} /* end check_XRC */
+       de_ccw->count = sizeof (struct DE_eckd_data);
+       de_ccw->flags |= CCW_FLAG_SLI;
+       return rc;
+}
 
-static inline void
+static int
 define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
              int totrk, int cmd, struct dasd_device * device)
 {
        struct dasd_eckd_private *private;
        struct ch_t geo, beg, end;
+       int rc = 0;
 
        private = (struct dasd_eckd_private *) device->private;
 
@@ -263,12 +228,12 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
        case DASD_ECKD_CCW_WRITE_KD_MT:
                data->mask.perm = 0x02;
                data->attributes.operation = private->attrib.operation;
-                check_XRC (ccw, data, device);
+               rc = check_XRC (ccw, data, device);
                break;
        case DASD_ECKD_CCW_WRITE_CKD:
        case DASD_ECKD_CCW_WRITE_CKD_MT:
                data->attributes.operation = DASD_BYPASS_CACHE;
-                check_XRC (ccw, data, device);
+               rc = check_XRC (ccw, data, device);
                break;
        case DASD_ECKD_CCW_ERASE:
        case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
@@ -276,7 +241,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
                data->mask.perm = 0x3;
                data->mask.auth = 0x1;
                data->attributes.operation = DASD_BYPASS_CACHE;
-                check_XRC (ccw, data, device);
+               rc = check_XRC (ccw, data, device);
                break;
        default:
                DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);
@@ -312,9 +277,10 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
        data->beg_ext.head = beg.head;
        data->end_ext.cyl = end.cyl;
        data->end_ext.head = end.head;
+       return rc;
 }
 
-static inline void
+static void
 locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
              int rec_on_trk, int no_rec, int cmd,
              struct dasd_device * device, int reclen)
@@ -484,6 +450,81 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
        return 0;
 }
 
+static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
+                                                   void *rcd_buffer,
+                                                   struct ciw *ciw, __u8 lpm)
+{
+       struct dasd_ccw_req *cqr;
+       struct ccw1 *ccw;
+
+       cqr = dasd_smalloc_request("ECKD", 1 /* RCD */, ciw->count, device);
+
+       if (IS_ERR(cqr)) {
+               DEV_MESSAGE(KERN_WARNING, device, "%s",
+                           "Could not allocate RCD request");
+               return cqr;
+       }
+
+       ccw = cqr->cpaddr;
+       ccw->cmd_code = ciw->cmd;
+       ccw->cda = (__u32)(addr_t)rcd_buffer;
+       ccw->count = ciw->count;
+
+       cqr->device = device;
+       cqr->expires = 10*HZ;
+       cqr->lpm = lpm;
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+       cqr->retries = 2;
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+       return cqr;
+}
+
+static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
+                                  void **rcd_buffer,
+                                  int *rcd_buffer_size, __u8 lpm)
+{
+       struct ciw *ciw;
+       char *rcd_buf = NULL;
+       int ret;
+       struct dasd_ccw_req *cqr;
+
+       /*
+        * scan for RCD command in extended SenseID data
+        */
+       ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
+       if (!ciw || ciw->cmd == 0) {
+               ret = -EOPNOTSUPP;
+               goto out_error;
+       }
+       rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
+       if (!rcd_buf) {
+               ret = -ENOMEM;
+               goto out_error;
+       }
+       cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
+       if (IS_ERR(cqr)) {
+               ret =  PTR_ERR(cqr);
+               goto out_error;
+       }
+       ret = dasd_sleep_on(cqr);
+       /*
+        * on success we update the user input parms
+        */
+       dasd_sfree_request(cqr, cqr->device);
+       if (ret)
+               goto out_error;
+
+       *rcd_buffer_size = ciw->count;
+       *rcd_buffer = rcd_buf;
+       return 0;
+out_error:
+       kfree(rcd_buf);
+       *rcd_buffer = NULL;
+       *rcd_buffer_size = 0;
+       return ret;
+}
+
 static int
 dasd_eckd_read_conf(struct dasd_device *device)
 {
@@ -503,8 +544,8 @@ dasd_eckd_read_conf(struct dasd_device *device)
        /* get configuration data per operational path */
        for (lpm = 0x80; lpm; lpm>>= 1) {
                if (lpm & path_data->opm){
-                       rc = read_conf_data_lpm(device->cdev, &conf_data,
-                                               &conf_len, lpm);
+                       rc = dasd_eckd_read_conf_lpm(device, &conf_data,
+                                                    &conf_len, lpm);
                        if (rc && rc != -EOPNOTSUPP) {  /* -EOPNOTSUPP is ok */
                                MESSAGE(KERN_WARNING,
                                        "Read configuration data returned "
@@ -548,7 +589,7 @@ dasd_eckd_read_conf(struct dasd_device *device)
 /*
  * Build CP for Perform Subsystem Function - SSC.
  */
-struct dasd_ccw_req *
+static struct dasd_ccw_req *
 dasd_eckd_build_psf_ssc(struct dasd_device *device)
 {
        struct dasd_ccw_req *cqr;
@@ -673,7 +714,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        /* Read Device Characteristics */
        rdc_data = (void *) &(private->rdc_data);
        memset(rdc_data, 0, sizeof(rdc_data));
-       rc = read_dev_chars(device->cdev, &rdc_data, 64);
+       rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
        if (rc)
                DEV_MESSAGE(KERN_WARNING, device,
                            "Read device characteristics returned "
@@ -1200,7 +1241,12 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
                return cqr;
        ccw = cqr->cpaddr;
        /* First ccw is define extent. */
-       define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device);
+       if (define_extent(ccw++, cqr->data, first_trk,
+                         last_trk, cmd, device) == -EAGAIN) {
+               /* Clock not in sync and XRC is enabled. Try again later. */
+               dasd_sfree_request(cqr, device);
+               return ERR_PTR(-EAGAIN);
+       }
        /* Build locate_record+read/write/ccws. */
        idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
        LO_data = (struct LO_eckd_data *) (idaws + cidaw);
@@ -1380,7 +1426,7 @@ dasd_eckd_release(struct dasd_device *device)
        cqr->device = device;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-       cqr->retries = 0;
+       cqr->retries = 2;       /* set retry counter to enable basic ERP */
        cqr->expires = 2 * HZ;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
@@ -1420,7 +1466,7 @@ dasd_eckd_reserve(struct dasd_device *device)
        cqr->device = device;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-       cqr->retries = 0;
+       cqr->retries = 2;       /* set retry counter to enable basic ERP */
        cqr->expires = 2 * HZ;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
@@ -1459,7 +1505,7 @@ dasd_eckd_steal_lock(struct dasd_device *device)
        cqr->device = device;
        clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-       cqr->retries = 0;
+       cqr->retries = 2;       /* set retry counter to enable basic ERP */
        cqr->expires = 2 * HZ;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
@@ -1609,7 +1655,7 @@ dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
  * Dump the range of CCWs into 'page' buffer
  * and return number of printed chars.
  */
-static inline int
+static int
 dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
 {
        int len, count;