linux/drivers/s390/cio/vfio_ccw_drv.c
<<
>>
Prefs
   1/*
   2 * VFIO based Physical Subchannel device driver
   3 *
   4 * Copyright IBM Corp. 2017
   5 *
   6 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
   7 *            Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/device.h>
  13#include <linux/slab.h>
  14#include <linux/uuid.h>
  15#include <linux/mdev.h>
  16
  17#include <asm/isc.h>
  18
  19#include "ioasm.h"
  20#include "css.h"
  21#include "vfio_ccw_private.h"
  22
  23struct workqueue_struct *vfio_ccw_work_q;
  24
  25/*
  26 * Helpers
  27 */
  28int vfio_ccw_sch_quiesce(struct subchannel *sch)
  29{
  30        struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
  31        DECLARE_COMPLETION_ONSTACK(completion);
  32        int iretry, ret = 0;
  33
  34        spin_lock_irq(sch->lock);
  35        if (!sch->schib.pmcw.ena)
  36                goto out_unlock;
  37        ret = cio_disable_subchannel(sch);
  38        if (ret != -EBUSY)
  39                goto out_unlock;
  40
  41        do {
  42                iretry = 255;
  43
  44                ret = cio_cancel_halt_clear(sch, &iretry);
  45                while (ret == -EBUSY) {
  46                        /*
  47                         * Flush all I/O and wait for
  48                         * cancel/halt/clear completion.
  49                         */
  50                        private->completion = &completion;
  51                        spin_unlock_irq(sch->lock);
  52
  53                        wait_for_completion_timeout(&completion, 3*HZ);
  54
  55                        spin_lock_irq(sch->lock);
  56                        private->completion = NULL;
  57                        flush_workqueue(vfio_ccw_work_q);
  58                        ret = cio_cancel_halt_clear(sch, &iretry);
  59                };
  60
  61                ret = cio_disable_subchannel(sch);
  62        } while (ret == -EBUSY);
  63out_unlock:
  64        private->state = VFIO_CCW_STATE_NOT_OPER;
  65        spin_unlock_irq(sch->lock);
  66        return ret;
  67}
  68
  69static void vfio_ccw_sch_io_todo(struct work_struct *work)
  70{
  71        struct vfio_ccw_private *private;
  72        struct subchannel *sch;
  73        struct irb *irb;
  74
  75        private = container_of(work, struct vfio_ccw_private, io_work);
  76        irb = &private->irb;
  77        sch = private->sch;
  78
  79        if (scsw_is_solicited(&irb->scsw)) {
  80                cp_update_scsw(&private->cp, &irb->scsw);
  81                cp_free(&private->cp);
  82        }
  83        memcpy(private->io_region.irb_area, irb, sizeof(*irb));
  84
  85        if (private->io_trigger)
  86                eventfd_signal(private->io_trigger, 1);
  87
  88        if (private->mdev)
  89                private->state = VFIO_CCW_STATE_IDLE;
  90}
  91
  92/*
  93 * Sysfs interfaces
  94 */
  95static ssize_t chpids_show(struct device *dev,
  96                           struct device_attribute *attr,
  97                           char *buf)
  98{
  99        struct subchannel *sch = to_subchannel(dev);
 100        struct chsc_ssd_info *ssd = &sch->ssd_info;
 101        ssize_t ret = 0;
 102        int chp;
 103        int mask;
 104
 105        for (chp = 0; chp < 8; chp++) {
 106                mask = 0x80 >> chp;
 107                if (ssd->path_mask & mask)
 108                        ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id);
 109                else
 110                        ret += sprintf(buf + ret, "00 ");
 111        }
 112        ret += sprintf(buf+ret, "\n");
 113        return ret;
 114}
 115
 116static ssize_t pimpampom_show(struct device *dev,
 117                              struct device_attribute *attr,
 118                              char *buf)
 119{
 120        struct subchannel *sch = to_subchannel(dev);
 121        struct pmcw *pmcw = &sch->schib.pmcw;
 122
 123        return sprintf(buf, "%02x %02x %02x\n",
 124                       pmcw->pim, pmcw->pam, pmcw->pom);
 125}
 126
 127static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
 128static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
 129
 130static struct attribute *vfio_subchannel_attrs[] = {
 131        &dev_attr_chpids.attr,
 132        &dev_attr_pimpampom.attr,
 133        NULL,
 134};
 135
 136static struct attribute_group vfio_subchannel_attr_group = {
 137        .attrs = vfio_subchannel_attrs,
 138};
 139
 140/*
 141 * Css driver callbacks
 142 */
 143static void vfio_ccw_sch_irq(struct subchannel *sch)
 144{
 145        struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
 146
 147        inc_irq_stat(IRQIO_CIO);
 148        vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_INTERRUPT);
 149}
 150
 151static int vfio_ccw_sch_probe(struct subchannel *sch)
 152{
 153        struct pmcw *pmcw = &sch->schib.pmcw;
 154        struct vfio_ccw_private *private;
 155        int ret;
 156
 157        if (pmcw->qf) {
 158                dev_warn(&sch->dev, "vfio: ccw: does not support QDIO: %s\n",
 159                         dev_name(&sch->dev));
 160                return -ENODEV;
 161        }
 162
 163        private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
 164        if (!private)
 165                return -ENOMEM;
 166        private->sch = sch;
 167        dev_set_drvdata(&sch->dev, private);
 168
 169        spin_lock_irq(sch->lock);
 170        private->state = VFIO_CCW_STATE_NOT_OPER;
 171        sch->isc = VFIO_CCW_ISC;
 172        ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
 173        spin_unlock_irq(sch->lock);
 174        if (ret)
 175                goto out_free;
 176
 177        ret = sysfs_create_group(&sch->dev.kobj, &vfio_subchannel_attr_group);
 178        if (ret)
 179                goto out_disable;
 180
 181        ret = vfio_ccw_mdev_reg(sch);
 182        if (ret)
 183                goto out_rm_group;
 184
 185        INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
 186        atomic_set(&private->avail, 1);
 187        private->state = VFIO_CCW_STATE_STANDBY;
 188
 189        return 0;
 190
 191out_rm_group:
 192        sysfs_remove_group(&sch->dev.kobj, &vfio_subchannel_attr_group);
 193out_disable:
 194        cio_disable_subchannel(sch);
 195out_free:
 196        dev_set_drvdata(&sch->dev, NULL);
 197        kfree(private);
 198        return ret;
 199}
 200
 201static int vfio_ccw_sch_remove(struct subchannel *sch)
 202{
 203        struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
 204
 205        vfio_ccw_sch_quiesce(sch);
 206
 207        vfio_ccw_mdev_unreg(sch);
 208
 209        sysfs_remove_group(&sch->dev.kobj, &vfio_subchannel_attr_group);
 210
 211        dev_set_drvdata(&sch->dev, NULL);
 212
 213        kfree(private);
 214
 215        return 0;
 216}
 217
 218static void vfio_ccw_sch_shutdown(struct subchannel *sch)
 219{
 220        vfio_ccw_sch_quiesce(sch);
 221}
 222
 223/**
 224 * vfio_ccw_sch_event - process subchannel event
 225 * @sch: subchannel
 226 * @process: non-zero if function is called in process context
 227 *
 228 * An unspecified event occurred for this subchannel. Adjust data according
 229 * to the current operational state of the subchannel. Return zero when the
 230 * event has been handled sufficiently or -EAGAIN when this function should
 231 * be called again in process context.
 232 */
 233static int vfio_ccw_sch_event(struct subchannel *sch, int process)
 234{
 235        struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
 236        unsigned long flags;
 237
 238        spin_lock_irqsave(sch->lock, flags);
 239        if (!device_is_registered(&sch->dev))
 240                goto out_unlock;
 241
 242        if (work_pending(&sch->todo_work))
 243                goto out_unlock;
 244
 245        if (cio_update_schib(sch)) {
 246                vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
 247                goto out_unlock;
 248        }
 249
 250        private = dev_get_drvdata(&sch->dev);
 251        if (private->state == VFIO_CCW_STATE_NOT_OPER) {
 252                private->state = private->mdev ? VFIO_CCW_STATE_IDLE :
 253                                 VFIO_CCW_STATE_STANDBY;
 254        }
 255
 256out_unlock:
 257        spin_unlock_irqrestore(sch->lock, flags);
 258
 259        return 0;
 260}
 261
 262static struct css_device_id vfio_ccw_sch_ids[] = {
 263        { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
 264        { /* end of list */ },
 265};
 266MODULE_DEVICE_TABLE(css, vfio_ccw_sch_ids);
 267
 268static struct css_driver vfio_ccw_sch_driver = {
 269        .drv = {
 270                .name = "vfio_ccw",
 271                .owner = THIS_MODULE,
 272        },
 273        .subchannel_type = vfio_ccw_sch_ids,
 274        .irq = vfio_ccw_sch_irq,
 275        .probe = vfio_ccw_sch_probe,
 276        .remove = vfio_ccw_sch_remove,
 277        .shutdown = vfio_ccw_sch_shutdown,
 278        .sch_event = vfio_ccw_sch_event,
 279};
 280
 281static int __init vfio_ccw_sch_init(void)
 282{
 283        int ret;
 284
 285        vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
 286        if (!vfio_ccw_work_q)
 287                return -ENOMEM;
 288
 289        isc_register(VFIO_CCW_ISC);
 290        ret = css_driver_register(&vfio_ccw_sch_driver);
 291        if (ret) {
 292                isc_unregister(VFIO_CCW_ISC);
 293                destroy_workqueue(vfio_ccw_work_q);
 294        }
 295
 296        return ret;
 297}
 298
 299static void __exit vfio_ccw_sch_exit(void)
 300{
 301        css_driver_unregister(&vfio_ccw_sch_driver);
 302        isc_unregister(VFIO_CCW_ISC);
 303        destroy_workqueue(vfio_ccw_work_q);
 304}
 305module_init(vfio_ccw_sch_init);
 306module_exit(vfio_ccw_sch_exit);
 307
 308MODULE_LICENSE("GPL v2");
 309