linux/drivers/block/viodasd.c
<<
>>
Prefs
   1/* -*- linux-c -*-
   2 * viodasd.c
   3 *  Authors: Dave Boutcher <boutcher@us.ibm.com>
   4 *           Ryan Arnold <ryanarn@us.ibm.com>
   5 *           Colin Devilbiss <devilbis@us.ibm.com>
   6 *           Stephen Rothwell
   7 *
   8 * (C) Copyright 2000-2004 IBM Corporation
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License as
  12 * published by the Free Software Foundation; either version 2 of the
  13 * License, or (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23 *
  24 * This routine provides access to disk space (termed "DASD" in historical
  25 * IBM terms) owned and managed by an OS/400 partition running on the
  26 * same box as this Linux partition.
  27 *
  28 * All disk operations are performed by sending messages back and forth to
  29 * the OS/400 partition.
  30 */
  31#include <linux/major.h>
  32#include <linux/fs.h>
  33#include <linux/module.h>
  34#include <linux/kernel.h>
  35#include <linux/blkdev.h>
  36#include <linux/genhd.h>
  37#include <linux/hdreg.h>
  38#include <linux/errno.h>
  39#include <linux/init.h>
  40#include <linux/string.h>
  41#include <linux/dma-mapping.h>
  42#include <linux/completion.h>
  43#include <linux/device.h>
  44#include <linux/scatterlist.h>
  45
  46#include <asm/uaccess.h>
  47#include <asm/vio.h>
  48#include <asm/iseries/hv_types.h>
  49#include <asm/iseries/hv_lp_event.h>
  50#include <asm/iseries/hv_lp_config.h>
  51#include <asm/iseries/vio.h>
  52#include <asm/firmware.h>
  53
  54MODULE_DESCRIPTION("iSeries Virtual DASD");
  55MODULE_AUTHOR("Dave Boutcher");
  56MODULE_LICENSE("GPL");
  57
  58/*
  59 * We only support 7 partitions per physical disk....so with minor
  60 * numbers 0-255 we get a maximum of 32 disks.
  61 */
  62#define VIOD_GENHD_NAME         "iseries/vd"
  63
  64#define VIOD_VERS               "1.64"
  65
  66#define VIOD_KERN_WARNING       KERN_WARNING "viod: "
  67#define VIOD_KERN_INFO          KERN_INFO "viod: "
  68
  69enum {
  70        PARTITION_SHIFT = 3,
  71        MAX_DISKNO = HVMAXARCHITECTEDVIRTUALDISKS,
  72        MAX_DISK_NAME = FIELD_SIZEOF(struct gendisk, disk_name)
  73};
  74
  75static DEFINE_SPINLOCK(viodasd_spinlock);
  76
  77#define VIOMAXREQ               16
  78
  79#define DEVICE_NO(cell) ((struct viodasd_device *)(cell) - &viodasd_devices[0])
  80
  81struct viodasd_waitevent {
  82        struct completion       com;
  83        int                     rc;
  84        u16                     sub_result;
  85        int                     max_disk;       /* open */
  86};
  87
  88static const struct vio_error_entry viodasd_err_table[] = {
  89        { 0x0201, EINVAL, "Invalid Range" },
  90        { 0x0202, EINVAL, "Invalid Token" },
  91        { 0x0203, EIO, "DMA Error" },
  92        { 0x0204, EIO, "Use Error" },
  93        { 0x0205, EIO, "Release Error" },
  94        { 0x0206, EINVAL, "Invalid Disk" },
  95        { 0x0207, EBUSY, "Cant Lock" },
  96        { 0x0208, EIO, "Already Locked" },
  97        { 0x0209, EIO, "Already Unlocked" },
  98        { 0x020A, EIO, "Invalid Arg" },
  99        { 0x020B, EIO, "Bad IFS File" },
 100        { 0x020C, EROFS, "Read Only Device" },
 101        { 0x02FF, EIO, "Internal Error" },
 102        { 0x0000, 0, NULL },
 103};
 104
 105/*
 106 * Figure out the biggest I/O request (in sectors) we can accept
 107 */
 108#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA)
 109
 110/*
 111 * Number of disk I/O requests we've sent to OS/400
 112 */
 113static int num_req_outstanding;
 114
 115/*
 116 * This is our internal structure for keeping track of disk devices
 117 */
 118struct viodasd_device {
 119        u16             cylinders;
 120        u16             tracks;
 121        u16             sectors;
 122        u16             bytes_per_sector;
 123        u64             size;
 124        int             read_only;
 125        spinlock_t      q_lock;
 126        struct gendisk  *disk;
 127        struct device   *dev;
 128} viodasd_devices[MAX_DISKNO];
 129
 130/*
 131 * External open entry point.
 132 */
 133static int viodasd_open(struct inode *ino, struct file *fil)
 134{
 135        struct viodasd_device *d = ino->i_bdev->bd_disk->private_data;
 136        HvLpEvent_Rc hvrc;
 137        struct viodasd_waitevent we;
 138        u16 flags = 0;
 139
 140        if (d->read_only) {
 141                if ((fil != NULL) && (fil->f_mode & FMODE_WRITE))
 142                        return -EROFS;
 143                flags = vioblockflags_ro;
 144        }
 145
 146        init_completion(&we.com);
 147
 148        /* Send the open event to OS/400 */
 149        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 150                        HvLpEvent_Type_VirtualIo,
 151                        viomajorsubtype_blockio | vioblockopen,
 152                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 153                        viopath_sourceinst(viopath_hostLp),
 154                        viopath_targetinst(viopath_hostLp),
 155                        (u64)(unsigned long)&we, VIOVERSION << 16,
 156                        ((u64)DEVICE_NO(d) << 48) | ((u64)flags << 32),
 157                        0, 0, 0);
 158        if (hvrc != 0) {
 159                printk(VIOD_KERN_WARNING "HV open failed %d\n", (int)hvrc);
 160                return -EIO;
 161        }
 162
 163        wait_for_completion(&we.com);
 164
 165        /* Check the return code */
 166        if (we.rc != 0) {
 167                const struct vio_error_entry *err =
 168                        vio_lookup_rc(viodasd_err_table, we.sub_result);
 169
 170                printk(VIOD_KERN_WARNING
 171                                "bad rc opening disk: %d:0x%04x (%s)\n",
 172                                (int)we.rc, we.sub_result, err->msg);
 173                return -EIO;
 174        }
 175
 176        return 0;
 177}
 178
 179/*
 180 * External release entry point.
 181 */
 182static int viodasd_release(struct inode *ino, struct file *fil)
 183{
 184        struct viodasd_device *d = ino->i_bdev->bd_disk->private_data;
 185        HvLpEvent_Rc hvrc;
 186
 187        /* Send the event to OS/400.  We DON'T expect a response */
 188        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 189                        HvLpEvent_Type_VirtualIo,
 190                        viomajorsubtype_blockio | vioblockclose,
 191                        HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
 192                        viopath_sourceinst(viopath_hostLp),
 193                        viopath_targetinst(viopath_hostLp),
 194                        0, VIOVERSION << 16,
 195                        ((u64)DEVICE_NO(d) << 48) /* | ((u64)flags << 32) */,
 196                        0, 0, 0);
 197        if (hvrc != 0)
 198                printk(VIOD_KERN_WARNING "HV close call failed %d\n",
 199                                (int)hvrc);
 200        return 0;
 201}
 202
 203
 204/* External ioctl entry point.
 205 */
 206static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 207{
 208        struct gendisk *disk = bdev->bd_disk;
 209        struct viodasd_device *d = disk->private_data;
 210
 211        geo->sectors = d->sectors ? d->sectors : 32;
 212        geo->heads = d->tracks ? d->tracks  : 64;
 213        geo->cylinders = d->cylinders ? d->cylinders :
 214                get_capacity(disk) / (geo->sectors * geo->heads);
 215
 216        return 0;
 217}
 218
 219/*
 220 * Our file operations table
 221 */
 222static struct block_device_operations viodasd_fops = {
 223        .owner = THIS_MODULE,
 224        .open = viodasd_open,
 225        .release = viodasd_release,
 226        .getgeo = viodasd_getgeo,
 227};
 228
 229/*
 230 * End a request
 231 */
 232static void viodasd_end_request(struct request *req, int error,
 233                int num_sectors)
 234{
 235        __blk_end_request(req, error, num_sectors << 9);
 236}
 237
 238/*
 239 * Send an actual I/O request to OS/400
 240 */
 241static int send_request(struct request *req)
 242{
 243        u64 start;
 244        int direction;
 245        int nsg;
 246        u16 viocmd;
 247        HvLpEvent_Rc hvrc;
 248        struct vioblocklpevent *bevent;
 249        struct HvLpEvent *hev;
 250        struct scatterlist sg[VIOMAXBLOCKDMA];
 251        int sgindex;
 252        int statindex;
 253        struct viodasd_device *d;
 254        unsigned long flags;
 255
 256        start = (u64)req->sector << 9;
 257
 258        if (rq_data_dir(req) == READ) {
 259                direction = DMA_FROM_DEVICE;
 260                viocmd = viomajorsubtype_blockio | vioblockread;
 261                statindex = 0;
 262        } else {
 263                direction = DMA_TO_DEVICE;
 264                viocmd = viomajorsubtype_blockio | vioblockwrite;
 265                statindex = 1;
 266        }
 267
 268        d = req->rq_disk->private_data;
 269
 270        /* Now build the scatter-gather list */
 271        sg_init_table(sg, VIOMAXBLOCKDMA);
 272        nsg = blk_rq_map_sg(req->q, req, sg);
 273        nsg = dma_map_sg(d->dev, sg, nsg, direction);
 274
 275        spin_lock_irqsave(&viodasd_spinlock, flags);
 276        num_req_outstanding++;
 277
 278        /* This optimization handles a single DMA block */
 279        if (nsg == 1)
 280                hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 281                                HvLpEvent_Type_VirtualIo, viocmd,
 282                                HvLpEvent_AckInd_DoAck,
 283                                HvLpEvent_AckType_ImmediateAck,
 284                                viopath_sourceinst(viopath_hostLp),
 285                                viopath_targetinst(viopath_hostLp),
 286                                (u64)(unsigned long)req, VIOVERSION << 16,
 287                                ((u64)DEVICE_NO(d) << 48), start,
 288                                ((u64)sg_dma_address(&sg[0])) << 32,
 289                                sg_dma_len(&sg[0]));
 290        else {
 291                bevent = (struct vioblocklpevent *)
 292                        vio_get_event_buffer(viomajorsubtype_blockio);
 293                if (bevent == NULL) {
 294                        printk(VIOD_KERN_WARNING
 295                               "error allocating disk event buffer\n");
 296                        goto error_ret;
 297                }
 298
 299                /*
 300                 * Now build up the actual request.  Note that we store
 301                 * the pointer to the request in the correlation
 302                 * token so we can match the response up later
 303                 */
 304                memset(bevent, 0, sizeof(struct vioblocklpevent));
 305                hev = &bevent->event;
 306                hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DO_ACK |
 307                        HV_LP_EVENT_INT;
 308                hev->xType = HvLpEvent_Type_VirtualIo;
 309                hev->xSubtype = viocmd;
 310                hev->xSourceLp = HvLpConfig_getLpIndex();
 311                hev->xTargetLp = viopath_hostLp;
 312                hev->xSizeMinus1 =
 313                        offsetof(struct vioblocklpevent, u.rw_data.dma_info) +
 314                        (sizeof(bevent->u.rw_data.dma_info[0]) * nsg) - 1;
 315                hev->xSourceInstanceId = viopath_sourceinst(viopath_hostLp);
 316                hev->xTargetInstanceId = viopath_targetinst(viopath_hostLp);
 317                hev->xCorrelationToken = (u64)req;
 318                bevent->version = VIOVERSION;
 319                bevent->disk = DEVICE_NO(d);
 320                bevent->u.rw_data.offset = start;
 321
 322                /*
 323                 * Copy just the dma information from the sg list
 324                 * into the request
 325                 */
 326                for (sgindex = 0; sgindex < nsg; sgindex++) {
 327                        bevent->u.rw_data.dma_info[sgindex].token =
 328                                sg_dma_address(&sg[sgindex]);
 329                        bevent->u.rw_data.dma_info[sgindex].len =
 330                                sg_dma_len(&sg[sgindex]);
 331                }
 332
 333                /* Send the request */
 334                hvrc = HvCallEvent_signalLpEvent(&bevent->event);
 335                vio_free_event_buffer(viomajorsubtype_blockio, bevent);
 336        }
 337
 338        if (hvrc != HvLpEvent_Rc_Good) {
 339                printk(VIOD_KERN_WARNING
 340                       "error sending disk event to OS/400 (rc %d)\n",
 341                       (int)hvrc);
 342                goto error_ret;
 343        }
 344        spin_unlock_irqrestore(&viodasd_spinlock, flags);
 345        return 0;
 346
 347error_ret:
 348        num_req_outstanding--;
 349        spin_unlock_irqrestore(&viodasd_spinlock, flags);
 350        dma_unmap_sg(d->dev, sg, nsg, direction);
 351        return -1;
 352}
 353
 354/*
 355 * This is the external request processing routine
 356 */
 357static void do_viodasd_request(struct request_queue *q)
 358{
 359        struct request *req;
 360
 361        /*
 362         * If we already have the maximum number of requests
 363         * outstanding to OS/400 just bail out. We'll come
 364         * back later.
 365         */
 366        while (num_req_outstanding < VIOMAXREQ) {
 367                req = elv_next_request(q);
 368                if (req == NULL)
 369                        return;
 370                /* dequeue the current request from the queue */
 371                blkdev_dequeue_request(req);
 372                /* check that request contains a valid command */
 373                if (!blk_fs_request(req)) {
 374                        viodasd_end_request(req, -EIO, req->hard_nr_sectors);
 375                        continue;
 376                }
 377                /* Try sending the request */
 378                if (send_request(req) != 0)
 379                        viodasd_end_request(req, -EIO, req->hard_nr_sectors);
 380        }
 381}
 382
 383/*
 384 * Probe a single disk and fill in the viodasd_device structure
 385 * for it.
 386 */
 387static int probe_disk(struct viodasd_device *d)
 388{
 389        HvLpEvent_Rc hvrc;
 390        struct viodasd_waitevent we;
 391        int dev_no = DEVICE_NO(d);
 392        struct gendisk *g;
 393        struct request_queue *q;
 394        u16 flags = 0;
 395
 396retry:
 397        init_completion(&we.com);
 398
 399        /* Send the open event to OS/400 */
 400        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 401                        HvLpEvent_Type_VirtualIo,
 402                        viomajorsubtype_blockio | vioblockopen,
 403                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
 404                        viopath_sourceinst(viopath_hostLp),
 405                        viopath_targetinst(viopath_hostLp),
 406                        (u64)(unsigned long)&we, VIOVERSION << 16,
 407                        ((u64)dev_no << 48) | ((u64)flags<< 32),
 408                        0, 0, 0);
 409        if (hvrc != 0) {
 410                printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc);
 411                return 0;
 412        }
 413
 414        wait_for_completion(&we.com);
 415
 416        if (we.rc != 0) {
 417                if (flags != 0)
 418                        return 0;
 419                /* try again with read only flag set */
 420                flags = vioblockflags_ro;
 421                goto retry;
 422        }
 423        if (we.max_disk > (MAX_DISKNO - 1)) {
 424                static int warned;
 425
 426                if (warned == 0) {
 427                        warned++;
 428                        printk(VIOD_KERN_INFO
 429                                "Only examining the first %d "
 430                                "of %d disks connected\n",
 431                                MAX_DISKNO, we.max_disk + 1);
 432                }
 433        }
 434
 435        /* Send the close event to OS/400.  We DON'T expect a response */
 436        hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
 437                        HvLpEvent_Type_VirtualIo,
 438                        viomajorsubtype_blockio | vioblockclose,
 439                        HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
 440                        viopath_sourceinst(viopath_hostLp),
 441                        viopath_targetinst(viopath_hostLp),
 442                        0, VIOVERSION << 16,
 443                        ((u64)dev_no << 48) | ((u64)flags << 32),
 444                        0, 0, 0);
 445        if (hvrc != 0) {
 446                printk(VIOD_KERN_WARNING
 447                       "bad rc sending event to OS/400 %d\n", (int)hvrc);
 448                return 0;
 449        }
 450
 451        if (d->dev == NULL) {
 452                /* this is when we reprobe for new disks */
 453                if (vio_create_viodasd(dev_no) == NULL) {
 454                        printk(VIOD_KERN_WARNING
 455                                "cannot allocate virtual device for disk %d\n",
 456                                dev_no);
 457                        return 0;
 458                }
 459                /*
 460                 * The vio_create_viodasd will have recursed into this
 461                 * routine with d->dev set to the new vio device and
 462                 * will finish the setup of the disk below.
 463                 */
 464                return 1;
 465        }
 466
 467        /* create the request queue for the disk */
 468        spin_lock_init(&d->q_lock);
 469        q = blk_init_queue(do_viodasd_request, &d->q_lock);
 470        if (q == NULL) {
 471                printk(VIOD_KERN_WARNING "cannot allocate queue for disk %d\n",
 472                                dev_no);
 473                return 0;
 474        }
 475        g = alloc_disk(1 << PARTITION_SHIFT);
 476        if (g == NULL) {
 477                printk(VIOD_KERN_WARNING
 478                                "cannot allocate disk structure for disk %d\n",
 479                                dev_no);
 480                blk_cleanup_queue(q);
 481                return 0;
 482        }
 483
 484        d->disk = g;
 485        blk_queue_max_hw_segments(q, VIOMAXBLOCKDMA);
 486        blk_queue_max_phys_segments(q, VIOMAXBLOCKDMA);
 487        blk_queue_max_sectors(q, VIODASD_MAXSECTORS);
 488        g->major = VIODASD_MAJOR;
 489        g->first_minor = dev_no << PARTITION_SHIFT;
 490        if (dev_no >= 26)
 491                snprintf(g->disk_name, sizeof(g->disk_name),
 492                                VIOD_GENHD_NAME "%c%c",
 493                                'a' + (dev_no / 26) - 1, 'a' + (dev_no % 26));
 494        else
 495                snprintf(g->disk_name, sizeof(g->disk_name),
 496                                VIOD_GENHD_NAME "%c", 'a' + (dev_no % 26));
 497        g->fops = &viodasd_fops;
 498        g->queue = q;
 499        g->private_data = d;
 500        g->driverfs_dev = d->dev;
 501        set_capacity(g, d->size >> 9);
 502
 503        printk(VIOD_KERN_INFO "disk %d: %lu sectors (%lu MB) "
 504                        "CHS=%d/%d/%d sector size %d%s\n",
 505                        dev_no, (unsigned long)(d->size >> 9),
 506                        (unsigned long)(d->size >> 20),
 507                        (int)d->cylinders, (int)d->tracks,
 508                        (int)d->sectors, (int)d->bytes_per_sector,
 509                        d->read_only ? " (RO)" : "");
 510
 511        /* register us in the global list */
 512        add_disk(g);
 513        return 1;
 514}
 515
 516/* returns the total number of scatterlist elements converted */
 517static int block_event_to_scatterlist(const struct vioblocklpevent *bevent,
 518                struct scatterlist *sg, int *total_len)
 519{
 520        int i, numsg;
 521        const struct rw_data *rw_data = &bevent->u.rw_data;
 522        static const int offset =
 523                offsetof(struct vioblocklpevent, u.rw_data.dma_info);
 524        static const int element_size = sizeof(rw_data->dma_info[0]);
 525
 526        numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size;
 527        if (numsg > VIOMAXBLOCKDMA)
 528                numsg = VIOMAXBLOCKDMA;
 529
 530        *total_len = 0;
 531        sg_init_table(sg, VIOMAXBLOCKDMA);
 532        for (i = 0; (i < numsg) && (rw_data->dma_info[i].len > 0); ++i) {
 533                sg_dma_address(&sg[i]) = rw_data->dma_info[i].token;
 534                sg_dma_len(&sg[i]) = rw_data->dma_info[i].len;
 535                *total_len += rw_data->dma_info[i].len;
 536        }
 537        return i;
 538}
 539
 540/*
 541 * Restart all queues, starting with the one _after_ the disk given,
 542 * thus reducing the chance of starvation of higher numbered disks.
 543 */
 544static void viodasd_restart_all_queues_starting_from(int first_index)
 545{
 546        int i;
 547
 548        for (i = first_index + 1; i < MAX_DISKNO; ++i)
 549                if (viodasd_devices[i].disk)
 550                        blk_run_queue(viodasd_devices[i].disk->queue);
 551        for (i = 0; i <= first_index; ++i)
 552                if (viodasd_devices[i].disk)
 553                        blk_run_queue(viodasd_devices[i].disk->queue);
 554}
 555
 556/*
 557 * For read and write requests, decrement the number of outstanding requests,
 558 * Free the DMA buffers we allocated.
 559 */
 560static int viodasd_handle_read_write(struct vioblocklpevent *bevent)
 561{
 562        int num_sg, num_sect, pci_direction, total_len;
 563        struct request *req;
 564        struct scatterlist sg[VIOMAXBLOCKDMA];
 565        struct HvLpEvent *event = &bevent->event;
 566        unsigned long irq_flags;
 567        struct viodasd_device *d;
 568        int error;
 569        spinlock_t *qlock;
 570
 571        num_sg = block_event_to_scatterlist(bevent, sg, &total_len);
 572        num_sect = total_len >> 9;
 573        if (event->xSubtype == (viomajorsubtype_blockio | vioblockread))
 574                pci_direction = DMA_FROM_DEVICE;
 575        else
 576                pci_direction = DMA_TO_DEVICE;
 577        req = (struct request *)bevent->event.xCorrelationToken;
 578        d = req->rq_disk->private_data;
 579
 580        dma_unmap_sg(d->dev, sg, num_sg, pci_direction);
 581
 582        /*
 583         * Since this is running in interrupt mode, we need to make sure
 584         * we're not stepping on any global I/O operations
 585         */
 586        spin_lock_irqsave(&viodasd_spinlock, irq_flags);
 587        num_req_outstanding--;
 588        spin_unlock_irqrestore(&viodasd_spinlock, irq_flags);
 589
 590        error = (event->xRc == HvLpEvent_Rc_Good) ? 0 : -EIO;
 591        if (error) {
 592                const struct vio_error_entry *err;
 593                err = vio_lookup_rc(viodasd_err_table, bevent->sub_result);
 594                printk(VIOD_KERN_WARNING "read/write error %d:0x%04x (%s)\n",
 595                                event->xRc, bevent->sub_result, err->msg);
 596                num_sect = req->hard_nr_sectors;
 597        }
 598        qlock = req->q->queue_lock;
 599        spin_lock_irqsave(qlock, irq_flags);
 600        viodasd_end_request(req, error, num_sect);
 601        spin_unlock_irqrestore(qlock, irq_flags);
 602
 603        /* Finally, try to get more requests off of this device's queue */
 604        viodasd_restart_all_queues_starting_from(DEVICE_NO(d));
 605
 606        return 0;
 607}
 608
 609/* This routine handles incoming block LP events */
 610static void handle_block_event(struct HvLpEvent *event)
 611{
 612        struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
 613        struct viodasd_waitevent *pwe;
 614
 615        if (event == NULL)
 616                /* Notification that a partition went away! */
 617                return;
 618        /* First, we should NEVER get an int here...only acks */
 619        if (hvlpevent_is_int(event)) {
 620                printk(VIOD_KERN_WARNING
 621                       "Yikes! got an int in viodasd event handler!\n");
 622                if (hvlpevent_need_ack(event)) {
 623                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 624                        HvCallEvent_ackLpEvent(event);
 625                }
 626        }
 627
 628        switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
 629        case vioblockopen:
 630                /*
 631                 * Handle a response to an open request.  We get all the
 632                 * disk information in the response, so update it.  The
 633                 * correlation token contains a pointer to a waitevent
 634                 * structure that has a completion in it.  update the
 635                 * return code in the waitevent structure and post the
 636                 * completion to wake up the guy who sent the request
 637                 */
 638                pwe = (struct viodasd_waitevent *)event->xCorrelationToken;
 639                pwe->rc = event->xRc;
 640                pwe->sub_result = bevent->sub_result;
 641                if (event->xRc == HvLpEvent_Rc_Good) {
 642                        const struct open_data *data = &bevent->u.open_data;
 643                        struct viodasd_device *device =
 644                                &viodasd_devices[bevent->disk];
 645                        device->read_only =
 646                                bevent->flags & vioblockflags_ro;
 647                        device->size = data->disk_size;
 648                        device->cylinders = data->cylinders;
 649                        device->tracks = data->tracks;
 650                        device->sectors = data->sectors;
 651                        device->bytes_per_sector = data->bytes_per_sector;
 652                        pwe->max_disk = data->max_disk;
 653                }
 654                complete(&pwe->com);
 655                break;
 656        case vioblockclose:
 657                break;
 658        case vioblockread:
 659        case vioblockwrite:
 660                viodasd_handle_read_write(bevent);
 661                break;
 662
 663        default:
 664                printk(VIOD_KERN_WARNING "invalid subtype!");
 665                if (hvlpevent_need_ack(event)) {
 666                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
 667                        HvCallEvent_ackLpEvent(event);
 668                }
 669        }
 670}
 671
 672/*
 673 * Get the driver to reprobe for more disks.
 674 */
 675static ssize_t probe_disks(struct device_driver *drv, const char *buf,
 676                size_t count)
 677{
 678        struct viodasd_device *d;
 679
 680        for (d = viodasd_devices; d < &viodasd_devices[MAX_DISKNO]; d++) {
 681                if (d->disk == NULL)
 682                        probe_disk(d);
 683        }
 684        return count;
 685}
 686static DRIVER_ATTR(probe, S_IWUSR, NULL, probe_disks);
 687
 688static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 689{
 690        struct viodasd_device *d = &viodasd_devices[vdev->unit_address];
 691
 692        d->dev = &vdev->dev;
 693        if (!probe_disk(d))
 694                return -ENODEV;
 695        return 0;
 696}
 697
 698static int viodasd_remove(struct vio_dev *vdev)
 699{
 700        struct viodasd_device *d;
 701
 702        d = &viodasd_devices[vdev->unit_address];
 703        if (d->disk) {
 704                del_gendisk(d->disk);
 705                blk_cleanup_queue(d->disk->queue);
 706                put_disk(d->disk);
 707                d->disk = NULL;
 708        }
 709        d->dev = NULL;
 710        return 0;
 711}
 712
 713/**
 714 * viodasd_device_table: Used by vio.c to match devices that we
 715 * support.
 716 */
 717static struct vio_device_id viodasd_device_table[] __devinitdata = {
 718        { "block", "IBM,iSeries-viodasd" },
 719        { "", "" }
 720};
 721MODULE_DEVICE_TABLE(vio, viodasd_device_table);
 722
 723static struct vio_driver viodasd_driver = {
 724        .id_table = viodasd_device_table,
 725        .probe = viodasd_probe,
 726        .remove = viodasd_remove,
 727        .driver = {
 728                .name = "viodasd",
 729                .owner = THIS_MODULE,
 730        }
 731};
 732
 733static int need_delete_probe;
 734
 735/*
 736 * Initialize the whole device driver.  Handle module and non-module
 737 * versions
 738 */
 739static int __init viodasd_init(void)
 740{
 741        int rc;
 742
 743        if (!firmware_has_feature(FW_FEATURE_ISERIES)) {
 744                rc = -ENODEV;
 745                goto early_fail;
 746        }
 747
 748        /* Try to open to our host lp */
 749        if (viopath_hostLp == HvLpIndexInvalid)
 750                vio_set_hostlp();
 751
 752        if (viopath_hostLp == HvLpIndexInvalid) {
 753                printk(VIOD_KERN_WARNING "invalid hosting partition\n");
 754                rc = -EIO;
 755                goto early_fail;
 756        }
 757
 758        printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n",
 759                        viopath_hostLp);
 760
 761        /* register the block device */
 762        rc =  register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
 763        if (rc) {
 764                printk(VIOD_KERN_WARNING
 765                                "Unable to get major number %d for %s\n",
 766                                VIODASD_MAJOR, VIOD_GENHD_NAME);
 767                goto early_fail;
 768        }
 769        /* Actually open the path to the hosting partition */
 770        rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio,
 771                                VIOMAXREQ + 2);
 772        if (rc) {
 773                printk(VIOD_KERN_WARNING
 774                       "error opening path to host partition %d\n",
 775                       viopath_hostLp);
 776                goto unregister_blk;
 777        }
 778
 779        /* Initialize our request handler */
 780        vio_setHandler(viomajorsubtype_blockio, handle_block_event);
 781
 782        rc = vio_register_driver(&viodasd_driver);
 783        if (rc) {
 784                printk(VIOD_KERN_WARNING "vio_register_driver failed\n");
 785                goto unset_handler;
 786        }
 787
 788        /*
 789         * If this call fails, it just means that we cannot dynamically
 790         * add virtual disks, but the driver will still work fine for
 791         * all existing disk, so ignore the failure.
 792         */
 793        if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe))
 794                need_delete_probe = 1;
 795
 796        return 0;
 797
 798unset_handler:
 799        vio_clearHandler(viomajorsubtype_blockio);
 800        viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
 801unregister_blk:
 802        unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
 803early_fail:
 804        return rc;
 805}
 806module_init(viodasd_init);
 807
 808void __exit viodasd_exit(void)
 809{
 810        if (need_delete_probe)
 811                driver_remove_file(&viodasd_driver.driver, &driver_attr_probe);
 812        vio_unregister_driver(&viodasd_driver);
 813        vio_clearHandler(viomajorsubtype_blockio);
 814        viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
 815        unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
 816}
 817module_exit(viodasd_exit);
 818