kfree(device);
                return ERR_PTR(-ENOMEM);
        }
+       /* Get two pages for ese format. */
+       device->ese_mem = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
+       if (!device->ese_mem) {
+               free_page((unsigned long) device->erp_mem);
+               free_pages((unsigned long) device->ccw_mem, 1);
+               kfree(device);
+               return ERR_PTR(-ENOMEM);
+       }
 
        dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
        dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
+       dasd_init_chunklist(&device->ese_chunks, device->ese_mem, PAGE_SIZE * 2);
        spin_lock_init(&device->mem_lock);
        atomic_set(&device->tasklet_scheduled, 0);
        tasklet_init(&device->tasklet, dasd_device_tasklet,
 void dasd_free_device(struct dasd_device *device)
 {
        kfree(device->private);
+       free_pages((unsigned long) device->ese_mem, 1);
        free_page((unsigned long) device->erp_mem);
        free_pages((unsigned long) device->ccw_mem, 1);
        kfree(device);
 }
 EXPORT_SYMBOL(dasd_smalloc_request);
 
+struct dasd_ccw_req *dasd_fmalloc_request(int magic, int cplength,
+                                         int datasize,
+                                         struct dasd_device *device)
+{
+       struct dasd_ccw_req *cqr;
+       unsigned long flags;
+       int size, cqr_size;
+       char *data;
+
+       cqr_size = (sizeof(*cqr) + 7L) & -8L;
+       size = cqr_size;
+       if (cplength > 0)
+               size += cplength * sizeof(struct ccw1);
+       if (datasize > 0)
+               size += datasize;
+
+       spin_lock_irqsave(&device->mem_lock, flags);
+       cqr = dasd_alloc_chunk(&device->ese_chunks, size);
+       spin_unlock_irqrestore(&device->mem_lock, flags);
+       if (!cqr)
+               return ERR_PTR(-ENOMEM);
+       memset(cqr, 0, sizeof(*cqr));
+       data = (char *)cqr + cqr_size;
+       cqr->cpaddr = NULL;
+       if (cplength > 0) {
+               cqr->cpaddr = data;
+               data += cplength * sizeof(struct ccw1);
+               memset(cqr->cpaddr, 0, cplength * sizeof(struct ccw1));
+       }
+       cqr->data = NULL;
+       if (datasize > 0) {
+               cqr->data = data;
+               memset(cqr->data, 0, datasize);
+       }
+
+       cqr->magic = magic;
+       set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+       dasd_get_device(device);
+
+       return cqr;
+}
+EXPORT_SYMBOL(dasd_fmalloc_request);
+
 void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
 {
        unsigned long flags;
 }
 EXPORT_SYMBOL(dasd_sfree_request);
 
+void dasd_ffree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&device->mem_lock, flags);
+       dasd_free_chunk(&device->ese_chunks, cqr);
+       spin_unlock_irqrestore(&device->mem_lock, flags);
+       dasd_put_device(device);
+}
+EXPORT_SYMBOL(dasd_ffree_request);
+
 /*
  * Check discipline magic in cqr.
  */
             irb->scsw.tm.sesq == SCSW_SESQ_PATH_NOFCX));
 }
 
+static int dasd_ese_needs_format(struct dasd_block *block, struct irb *irb)
+{
+       struct dasd_device *device = NULL;
+       u8 *sense = NULL;
+
+       if (!block)
+               return 0;
+       device = block->base;
+       if (!device || !device->discipline->is_ese)
+               return 0;
+       if (!device->discipline->is_ese(device))
+               return 0;
+
+       sense = dasd_get_sense(irb);
+       if (!sense)
+               return 0;
+
+       return !!(sense[1] & SNS1_NO_REC_FOUND) ||
+               !!(sense[1] & SNS1_FILE_PROTECTED) ||
+               scsw_cstat(&irb->scsw) == SCHN_STAT_INCORR_LEN;
+}
+
 /*
  * Interrupt handler for "normal" ssch-io based dasd devices.
  */
 void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
                      struct irb *irb)
 {
-       struct dasd_ccw_req *cqr, *next;
+       struct dasd_ccw_req *cqr, *next, *fcqr;
        struct dasd_device *device;
        unsigned long now;
        int nrf_suppressed = 0;
                return;
        }
 
+       if (dasd_ese_needs_format(cqr->block, irb)) {
+               if (rq_data_dir((struct request *)cqr->callback_data) == READ) {
+                       device->discipline->ese_read(cqr);
+                       cqr->status = DASD_CQR_SUCCESS;
+                       cqr->stopclk = now;
+                       dasd_device_clear_timer(device);
+                       dasd_schedule_device_bh(device);
+                       return;
+               }
+               fcqr = device->discipline->ese_format(device, cqr);
+               if (IS_ERR(fcqr)) {
+                       /*
+                        * If we can't format now, let the request go
+                        * one extra round. Maybe we can format later.
+                        */
+                       cqr->status = DASD_CQR_QUEUED;
+               } else {
+                       fcqr->status = DASD_CQR_QUEUED;
+                       cqr->status = DASD_CQR_QUEUED;
+                       list_add(&fcqr->devlist, &device->ccw_queue);
+                       dasd_schedule_device_bh(device);
+                       return;
+               }
+       }
+
        /* Check for clear pending */
        if (cqr->status == DASD_CQR_CLEAR_PENDING &&
            scsw_fctl(&irb->scsw) & SCSW_FCTL_CLEAR_FUNC) {
 
         */
        itcw_size = itcw_calc_size(0, count, 0);
 
-       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev,
-                                  NULL);
+       cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
        if (IS_ERR(cqr))
                return cqr;
 
        }
        cplength += count;
 
-       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
-                                  startdev, NULL);
+       cqr = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
        if (IS_ERR(cqr))
                return cqr;
 
 }
 
 static struct dasd_ccw_req *
-dasd_eckd_build_format(struct dasd_device *base,
-                      struct format_data_t *fdata,
-                      int enable_pav)
+dasd_eckd_build_format(struct dasd_device *base, struct dasd_device *startdev,
+                      struct format_data_t *fdata, int enable_pav)
 {
        struct dasd_eckd_private *base_priv;
        struct dasd_eckd_private *start_priv;
-       struct dasd_device *startdev = NULL;
        struct dasd_ccw_req *fcp;
        struct eckd_count *ect;
        struct ch_t address;
                         fdata->intensity);
                return ERR_PTR(-EINVAL);
        }
-       /* Allocate the format ccw request. */
-       fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength,
-                                  datasize, startdev, NULL);
+
+       fcp = dasd_fmalloc_request(DASD_ECKD_MAGIC, cplength, datasize, startdev);
        if (IS_ERR(fcp))
                return fcp;
 
        struct dasd_ccw_req *ccw_req;
 
        if (!fmt_buffer) {
-               ccw_req = dasd_eckd_build_format(base, fdata, enable_pav);
+               ccw_req = dasd_eckd_build_format(base, NULL, fdata, enable_pav);
        } else {
                if (tpm)
                        ccw_req = dasd_eckd_build_check_tcw(base, fdata,
                                rc = -EIO;
                        }
                        list_del_init(&cqr->blocklist);
-                       dasd_sfree_request(cqr, device);
+                       dasd_ffree_request(cqr, device);
                        private->count--;
                }
 
                                             0, NULL);
 }
 
+/*
+ * Callback function to free ESE format requests.
+ */
+static void dasd_eckd_ese_format_cb(struct dasd_ccw_req *cqr, void *data)
+{
+       struct dasd_device *device = cqr->startdev;
+       struct dasd_eckd_private *private = device->private;
+
+       private->count--;
+       dasd_ffree_request(cqr, device);
+}
+
+static struct dasd_ccw_req *
+dasd_eckd_ese_format(struct dasd_device *startdev, struct dasd_ccw_req *cqr)
+{
+       struct dasd_eckd_private *private;
+       struct format_data_t fdata;
+       unsigned int recs_per_trk;
+       struct dasd_ccw_req *fcqr;
+       struct dasd_device *base;
+       struct dasd_block *block;
+       unsigned int blksize;
+       struct request *req;
+       sector_t first_trk;
+       sector_t last_trk;
+       int rc;
+
+       req = cqr->callback_data;
+       base = cqr->block->base;
+       private = base->private;
+       block = base->block;
+       blksize = block->bp_block;
+       recs_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
+
+       first_trk = blk_rq_pos(req) >> block->s2b_shift;
+       sector_div(first_trk, recs_per_trk);
+       last_trk =
+               (blk_rq_pos(req) + blk_rq_sectors(req) - 1) >> block->s2b_shift;
+       sector_div(last_trk, recs_per_trk);
+
+       fdata.start_unit = first_trk;
+       fdata.stop_unit = last_trk;
+       fdata.blksize = blksize;
+       fdata.intensity = private->uses_cdl ? DASD_FMT_INT_COMPAT : 0;
+
+       rc = dasd_eckd_format_sanity_checks(base, &fdata);
+       if (rc)
+               return ERR_PTR(-EINVAL);
+
+       /*
+        * We're building the request with PAV disabled as we're reusing
+        * the former startdev.
+        */
+       fcqr = dasd_eckd_build_format(base, startdev, &fdata, 0);
+       if (IS_ERR(fcqr))
+               return fcqr;
+
+       fcqr->callback = dasd_eckd_ese_format_cb;
+
+       return fcqr;
+}
+
+/*
+ * When data is read from an unformatted area of an ESE volume, this function
+ * returns zeroed data and thereby mimics a read of zero data.
+ */
+static void dasd_eckd_ese_read(struct dasd_ccw_req *cqr)
+{
+       unsigned int blksize, off;
+       struct dasd_device *base;
+       struct req_iterator iter;
+       struct request *req;
+       struct bio_vec bv;
+       char *dst;
+
+       req = (struct request *) cqr->callback_data;
+       base = cqr->block->base;
+       blksize = base->block->bp_block;
+
+       rq_for_each_segment(bv, req, iter) {
+               dst = page_address(bv.bv_page) + bv.bv_offset;
+               for (off = 0; off < bv.bv_len; off += blksize) {
+                       if (dst && rq_data_dir(req) == READ) {
+                               dst += off;
+                               memset(dst, 0, blksize);
+                       }
+               }
+       }
+}
+
 /*
  * Helper function to count consecutive records of a single track.
  */
        cqr->retries = startdev->default_retries;
        cqr->buildclk = get_tod_clock();
        cqr->status = DASD_CQR_FILLED;
+
+       /* Set flags to suppress output for expected errors */
+       if (dasd_eckd_is_ese(basedev)) {
+               set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
+               set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
+               set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
+       }
+
        return cqr;
 }
 
        cqr->retries = startdev->default_retries;
        cqr->buildclk = get_tod_clock();
        cqr->status = DASD_CQR_FILLED;
+
+       /* Set flags to suppress output for expected errors */
+       if (dasd_eckd_is_ese(basedev))
+               set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
+
        return cqr;
 }
 
        cqr->retries = startdev->default_retries;
        cqr->buildclk = get_tod_clock();
        cqr->status = DASD_CQR_FILLED;
+
+       /* Set flags to suppress output for expected errors */
+       if (dasd_eckd_is_ese(basedev)) {
+               set_bit(DASD_CQR_SUPPRESS_FP, &cqr->flags);
+               set_bit(DASD_CQR_SUPPRESS_IL, &cqr->flags);
+               set_bit(DASD_CQR_SUPPRESS_NRF, &cqr->flags);
+       }
+
        return cqr;
 out_error:
        dasd_sfree_request(cqr, startdev);
        .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel,
        .ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld,
        .ext_pool_oos = dasd_eckd_ext_pool_oos,
+       .ese_format = dasd_eckd_ese_format,
+       .ese_read = dasd_eckd_ese_read,
 };
 
 static int __init