return 0;
 }
 
+static void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head)
+{
+       geo->cyl = (__u16) cyl;
+       geo->head = cyl >> 16;
+       geo->head <<= 4;
+       geo->head |= head;
+}
+
 static int
 check_XRC (struct ccw1         *de_ccw,
            struct DE_eckd_data *data,
 }
 
 static int
-define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
-             int totrk, int cmd, struct dasd_device * device)
+define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
+             unsigned int totrk, int cmd, struct dasd_device *device)
 {
        struct dasd_eckd_private *private;
-       struct ch_t geo, beg, end;
+       u32 begcyl, endcyl;
+       u16 heads, beghead, endhead;
        int rc = 0;
 
        private = (struct dasd_eckd_private *) device->private;
            && !(private->uses_cdl && trk < 2))
                data->ga_extended |= 0x40; /* Regular Data Format Mode */
 
-       geo.cyl = private->rdc_data.no_cyl;
-       geo.head = private->rdc_data.trk_per_cyl;
-       beg.cyl = trk / geo.head;
-       beg.head = trk % geo.head;
-       end.cyl = totrk / geo.head;
-       end.head = totrk % geo.head;
+       heads = private->rdc_data.trk_per_cyl;
+       begcyl = trk / heads;
+       beghead = trk % heads;
+       endcyl = totrk / heads;
+       endhead = totrk % heads;
 
        /* check for sequential prestage - enhance cylinder range */
        if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
            data->attributes.operation == DASD_SEQ_ACCESS) {
 
-               if (end.cyl + private->attrib.nr_cyl < geo.cyl)
-                       end.cyl += private->attrib.nr_cyl;
+               if (endcyl + private->attrib.nr_cyl < private->real_cyl)
+                       endcyl += private->attrib.nr_cyl;
                else
-                       end.cyl = (geo.cyl - 1);
+                       endcyl = (private->real_cyl - 1);
        }
 
-       data->beg_ext.cyl = beg.cyl;
-       data->beg_ext.head = beg.head;
-       data->end_ext.cyl = end.cyl;
-       data->end_ext.head = end.head;
+       set_ch_t(&data->beg_ext, begcyl, beghead);
+       set_ch_t(&data->end_ext, endcyl, endhead);
        return rc;
 }
 
        return rc;
 }
 
-static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
-                 int totrk, int cmd, struct dasd_device *basedev,
-                 struct dasd_device *startdev)
+static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata,
+                 unsigned int trk, unsigned int totrk, int cmd,
+                 struct dasd_device *basedev, struct dasd_device *startdev)
 {
        struct dasd_eckd_private *basepriv, *startpriv;
        struct DE_eckd_data *data;
-       struct ch_t geo, beg, end;
+       u32 begcyl, endcyl;
+       u16 heads, beghead, endhead;
        int rc = 0;
 
        basepriv = (struct dasd_eckd_private *) basedev->private;
            && !(basepriv->uses_cdl && trk < 2))
                data->ga_extended |= 0x40; /* Regular Data Format Mode */
 
-       geo.cyl = basepriv->rdc_data.no_cyl;
-       geo.head = basepriv->rdc_data.trk_per_cyl;
-       beg.cyl = trk / geo.head;
-       beg.head = trk % geo.head;
-       end.cyl = totrk / geo.head;
-       end.head = totrk % geo.head;
+       heads = basepriv->rdc_data.trk_per_cyl;
+       begcyl = trk / heads;
+       beghead = trk % heads;
+       endcyl = totrk / heads;
+       endhead = totrk % heads;
 
        /* check for sequential prestage - enhance cylinder range */
        if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
            data->attributes.operation == DASD_SEQ_ACCESS) {
 
-               if (end.cyl + basepriv->attrib.nr_cyl < geo.cyl)
-                       end.cyl += basepriv->attrib.nr_cyl;
+               if (endcyl + basepriv->attrib.nr_cyl < basepriv->real_cyl)
+                       endcyl += basepriv->attrib.nr_cyl;
                else
-                       end.cyl = (geo.cyl - 1);
+                       endcyl = (basepriv->real_cyl - 1);
        }
 
-       data->beg_ext.cyl = beg.cyl;
-       data->beg_ext.head = beg.head;
-       data->end_ext.cyl = end.cyl;
-       data->end_ext.head = end.head;
+       set_ch_t(&data->beg_ext, begcyl, beghead);
+       set_ch_t(&data->end_ext, endcyl, endhead);
        return rc;
 }
 
 static void
-locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
-             int rec_on_trk, int no_rec, int cmd,
+locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, unsigned int trk,
+             unsigned int rec_on_trk, int no_rec, int cmd,
              struct dasd_device * device, int reclen)
 {
        struct dasd_eckd_private *private;
        default:
                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;
-       data->seek_addr.head = data->search_arg.head =
-               trk % private->rdc_data.trk_per_cyl;
+       set_ch_t(&data->seek_addr,
+                trk / private->rdc_data.trk_per_cyl,
+                trk % private->rdc_data.trk_per_cyl);
+       data->search_arg.cyl = data->seek_addr.cyl;
+       data->search_arg.head = data->seek_addr.head;
        data->search_arg.record = rec_on_trk;
 }
 
                            "rc=%d", rc);
                goto out_err3;
        }
+       /* find the vaild cylinder size */
+       if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
+           private->rdc_data.long_no_cyl)
+               private->real_cyl = private->rdc_data.long_no_cyl;
+       else
+               private->real_cyl = private->rdc_data.no_cyl;
+
        DEV_MESSAGE(KERN_INFO, device,
                    "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
                    private->rdc_data.dev_type,
                    private->rdc_data.dev_model,
                    private->rdc_data.cu_type,
                    private->rdc_data.cu_model.model,
-                   private->rdc_data.no_cyl,
+                   private->real_cyl,
                    private->rdc_data.trk_per_cyl,
                    private->rdc_data.sec_per_trk);
        return 0;
        }
 
        private->uses_cdl = 1;
-       /* Calculate number of blocks/records per track. */
-       blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
        /* Check Track 0 for Compatible Disk Layout */
        count_area = NULL;
        for (i = 0; i < 3; i++) {
                block->s2b_shift++;
 
        blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
-       block->blocks = (private->rdc_data.no_cyl *
+       block->blocks = (private->real_cyl *
                          private->rdc_data.trk_per_cyl *
                          blk_per_trk);
 
        DEV_MESSAGE(KERN_INFO, device,
                    "(%dkB blks): %dkB at %dkB/trk %s",
                    (block->bp_block >> 10),
-                   ((private->rdc_data.no_cyl *
+                   ((private->real_cyl *
                      private->rdc_data.trk_per_cyl *
                      blk_per_trk * (block->bp_block >> 9)) >> 1),
                    ((blk_per_trk * block->bp_block) >> 10),
        struct eckd_count *ect;
        struct ccw1 *ccw;
        void *data;
-       int rpt, cyl, head;
+       int rpt;
+       struct ch_t address;
        int cplength, datasize;
        int i;
        int intensity = 0;
 
        private = (struct dasd_eckd_private *) device->private;
        rpt = recs_per_track(&private->rdc_data, 0, fdata->blksize);
-       cyl = fdata->start_unit / private->rdc_data.trk_per_cyl;
-       head = fdata->start_unit % private->rdc_data.trk_per_cyl;
+       set_ch_t(&address,
+                fdata->start_unit / private->rdc_data.trk_per_cyl,
+                fdata->start_unit % private->rdc_data.trk_per_cyl);
 
        /* Sanity checks. */
        if (fdata->start_unit >=
-           (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)) {
-               DEV_MESSAGE(KERN_INFO, device, "Track no %d too big!",
+           (private->real_cyl * private->rdc_data.trk_per_cyl)) {
+               DEV_MESSAGE(KERN_INFO, device, "Track no %u too big!",
                            fdata->start_unit);
                return ERR_PTR(-EINVAL);
        }
        if (fdata->start_unit > fdata->stop_unit) {
-               DEV_MESSAGE(KERN_INFO, device, "Track %d reached! ending.",
+               DEV_MESSAGE(KERN_INFO, device, "Track %u reached! ending.",
                            fdata->start_unit);
                return ERR_PTR(-EINVAL);
        }
        if (dasd_check_blocksize(fdata->blksize) != 0) {
                DEV_MESSAGE(KERN_WARNING, device,
-                           "Invalid blocksize %d...terminating!",
+                           "Invalid blocksize %u...terminating!",
                            fdata->blksize);
                return ERR_PTR(-EINVAL);
        }
        if (intensity & 0x01) { /* write record zero */
                ect = (struct eckd_count *) data;
                data += sizeof(struct eckd_count);
-               ect->cyl = cyl;
-               ect->head = head;
+               ect->cyl = address.cyl;
+               ect->head = address.head;
                ect->record = 0;
                ect->kl = 0;
                ect->dl = 8;
        if ((intensity & ~0x08) & 0x04) {       /* erase track */
                ect = (struct eckd_count *) data;
                data += sizeof(struct eckd_count);
-               ect->cyl = cyl;
-               ect->head = head;
+               ect->cyl = address.cyl;
+               ect->head = address.head;
                ect->record = 1;
                ect->kl = 0;
                ect->dl = 0;
                for (i = 0; i < rpt; i++) {
                        ect = (struct eckd_count *) data;
                        data += sizeof(struct eckd_count);
-                       ect->cyl = cyl;
-                       ect->head = head;
+                       ect->cyl = address.cyl;
+                       ect->head = address.head;
                        ect->record = i + 1;
                        ect->kl = 0;
                        ect->dl = fdata->blksize;
 
  * compute the block number from a
  * cyl-cyl-head-head structure
  */
-static inline int
+static sector_t
 cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
-        return ptr->cc * geo->heads * geo->sectors +
-              ptr->hh * geo->sectors;
+
+       sector_t cyl;
+       __u16 head;
+
+       /*decode cylinder and heads for large volumes */
+       cyl = ptr->hh & 0xFFF0;
+       cyl <<= 12;
+       cyl |= ptr->cc;
+       head = ptr->hh & 0x000F;
+       return cyl * geo->heads * geo->sectors +
+              head * geo->sectors;
 }
 
 /*
  * compute the block number from a
  * cyl-cyl-head-head-block structure
  */
-static inline int
+static sector_t
 cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
-        return ptr->cc * geo->heads * geo->sectors +
-               ptr->hh * geo->sectors +
+
+       sector_t cyl;
+       __u16 head;
+
+       /*decode cylinder and heads for large volumes */
+       cyl = ptr->hh & 0xFFF0;
+       cyl <<= 12;
+       cyl |= ptr->cc;
+       head = ptr->hh & 0x000F;
+       return  cyl * geo->heads * geo->sectors +
+               head * geo->sectors +
                ptr->b;
 }
 
 int
 ibm_partition(struct parsed_partitions *state, struct block_device *bdev)
 {
-       int blocksize, offset, size,res;
-       loff_t i_size;
+       int blocksize, res;
+       loff_t i_size, offset, size, fmt_size;
        dasd_information2_t *info;
        struct hd_geometry *geo;
        char type[5] = {0,};
        char name[7] = {0,};
        union label_t {
-               struct vtoc_volume_label vol;
+               struct vtoc_volume_label_cdl vol;
+               struct vtoc_volume_label_ldl lnx;
                struct vtoc_cms_label cms;
        } *label;
        unsigned char *data;
        if (data == NULL)
                goto out_readerr;
 
-       strncpy (type, data, 4);
-       if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD")))
-               strncpy(name, data + 8, 6);
-       else
-               strncpy(name, data + 4, 6);
        memcpy(label, data, sizeof(union label_t));
        put_dev_sector(sect);
 
+       if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
+               strncpy(type, label->vol.vollbl, 4);
+               strncpy(name, label->vol.volid, 6);
+       } else {
+               strncpy(type, label->lnx.vollbl, 4);
+               strncpy(name, label->lnx.volid, 6);
+       }
        EBCASC(type, 4);
        EBCASC(name, 6);
 
                        /*
                         * VM style CMS1 labeled disk
                         */
+                       blocksize = label->cms.block_size;
                        if (label->cms.disk_offset != 0) {
                                printk("CMS1/%8s(MDSK):", name);
                                /* disk is reserved minidisk */
-                               blocksize = label->cms.block_size;
                                offset = label->cms.disk_offset;
                                size = (label->cms.block_count - 1)
                                        * (blocksize >> 9);
                        } else {
                                printk("CMS1/%8s:", name);
                                offset = (info->label_block + 1);
-                               size = i_size >> 9;
+                               size = label->cms.block_count
+                                       * (blocksize >> 9);
                        }
+                       put_partition(state, 1, offset*(blocksize >> 9),
+                                     size-offset*(blocksize >> 9));
                } else {
-                       /*
-                        * Old style LNX1 or unlabeled disk
-                        */
-                       if (strncmp(type, "LNX1", 4) == 0)
-                               printk ("LNX1/%8s:", name);
-                       else
+                       if (strncmp(type, "LNX1", 4) == 0) {
+                               printk("LNX1/%8s:", name);
+                               if (label->lnx.ldl_version == 0xf2) {
+                                       fmt_size = label->lnx.formatted_blocks
+                                               * (blocksize >> 9);
+                               } else if (!strcmp(info->type, "ECKD")) {
+                                       /* formated w/o large volume support */
+                                       fmt_size = geo->cylinders * geo->heads
+                                             * geo->sectors * (blocksize >> 9);
+                               } else {
+                                       /* old label and no usable disk geometry
+                                        * (e.g. DIAG) */
+                                       fmt_size = i_size >> 9;
+                               }
+                               size = i_size >> 9;
+                               if (fmt_size < size)
+                                       size = fmt_size;
+                               offset = (info->label_block + 1);
+                       } else {
+                               /* unlabeled disk */
                                printk("(nonl)");
-                       offset = (info->label_block + 1);
-                       size = i_size >> 9;
-               }
-               put_partition(state, 1, offset*(blocksize >> 9),
+                               size = i_size >> 9;
+                               offset = (info->label_block + 1);
+                       }
+                       put_partition(state, 1, offset*(blocksize >> 9),
                                      size-offset*(blocksize >> 9));
+               }
        } else if (info->format == DASD_FORMAT_CDL) {
                /*
                 * New style CDL formatted disk
                 */
-               unsigned int blk;
+               sector_t blk;
                int counter;
 
                /*
                                /* skip FMT4 / FMT5 / FMT7 labels */
                                if (f1.DS1FMTID == _ascebc['4']
                                    || f1.DS1FMTID == _ascebc['5']
-                                   || f1.DS1FMTID == _ascebc['7']) {
+                                   || f1.DS1FMTID == _ascebc['7']
+                                   || f1.DS1FMTID == _ascebc['9']) {
                                        blk++;
                                        data = read_dev_sector(bdev, blk *
                                                               (blocksize/512),
                                        continue;
                                }
 
-                               /* only FMT1 valid at this point */
-                               if (f1.DS1FMTID != _ascebc['1'])
+                               /* only FMT1 and 8 labels valid at this point */
+                               if (f1.DS1FMTID != _ascebc['1'] &&
+                                   f1.DS1FMTID != _ascebc['8'])
                                        break;
 
                                /* OK, we got valid partition data */