root/drivers/s390/cio/vfio_ccw_fsm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fsm_io_helper
  2. fsm_do_halt
  3. fsm_do_clear
  4. fsm_notoper
  5. fsm_nop
  6. fsm_io_error
  7. fsm_io_busy
  8. fsm_io_retry
  9. fsm_async_error
  10. fsm_async_retry
  11. fsm_disabled_irq
  12. get_schid
  13. fsm_io_request
  14. fsm_async_request
  15. fsm_irq

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Finite state machine for vfio-ccw device handling
   4  *
   5  * Copyright IBM Corp. 2017
   6  * Copyright Red Hat, Inc. 2019
   7  *
   8  * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
   9  *            Cornelia Huck <cohuck@redhat.com>
  10  */
  11 
  12 #include <linux/vfio.h>
  13 #include <linux/mdev.h>
  14 
  15 #include "ioasm.h"
  16 #include "vfio_ccw_private.h"
  17 
  18 #define CREATE_TRACE_POINTS
  19 #include "vfio_ccw_trace.h"
  20 
  21 static int fsm_io_helper(struct vfio_ccw_private *private)
  22 {
  23         struct subchannel *sch;
  24         union orb *orb;
  25         int ccode;
  26         __u8 lpm;
  27         unsigned long flags;
  28         int ret;
  29 
  30         sch = private->sch;
  31 
  32         spin_lock_irqsave(sch->lock, flags);
  33 
  34         orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);
  35         if (!orb) {
  36                 ret = -EIO;
  37                 goto out;
  38         }
  39 
  40         VFIO_CCW_TRACE_EVENT(5, "stIO");
  41         VFIO_CCW_TRACE_EVENT(5, dev_name(&sch->dev));
  42 
  43         /* Issue "Start Subchannel" */
  44         ccode = ssch(sch->schid, orb);
  45 
  46         VFIO_CCW_HEX_EVENT(5, &ccode, sizeof(ccode));
  47 
  48         switch (ccode) {
  49         case 0:
  50                 /*
  51                  * Initialize device status information
  52                  */
  53                 sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
  54                 ret = 0;
  55                 private->state = VFIO_CCW_STATE_CP_PENDING;
  56                 break;
  57         case 1:         /* Status pending */
  58         case 2:         /* Busy */
  59                 ret = -EBUSY;
  60                 break;
  61         case 3:         /* Device/path not operational */
  62         {
  63                 lpm = orb->cmd.lpm;
  64                 if (lpm != 0)
  65                         sch->lpm &= ~lpm;
  66                 else
  67                         sch->lpm = 0;
  68 
  69                 if (cio_update_schib(sch))
  70                         ret = -ENODEV;
  71                 else
  72                         ret = sch->lpm ? -EACCES : -ENODEV;
  73                 break;
  74         }
  75         default:
  76                 ret = ccode;
  77         }
  78 out:
  79         spin_unlock_irqrestore(sch->lock, flags);
  80         return ret;
  81 }
  82 
  83 static int fsm_do_halt(struct vfio_ccw_private *private)
  84 {
  85         struct subchannel *sch;
  86         unsigned long flags;
  87         int ccode;
  88         int ret;
  89 
  90         sch = private->sch;
  91 
  92         spin_lock_irqsave(sch->lock, flags);
  93 
  94         VFIO_CCW_TRACE_EVENT(2, "haltIO");
  95         VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
  96 
  97         /* Issue "Halt Subchannel" */
  98         ccode = hsch(sch->schid);
  99 
 100         VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode));
 101 
 102         switch (ccode) {
 103         case 0:
 104                 /*
 105                  * Initialize device status information
 106                  */
 107                 sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
 108                 ret = 0;
 109                 break;
 110         case 1:         /* Status pending */
 111         case 2:         /* Busy */
 112                 ret = -EBUSY;
 113                 break;
 114         case 3:         /* Device not operational */
 115                 ret = -ENODEV;
 116                 break;
 117         default:
 118                 ret = ccode;
 119         }
 120         spin_unlock_irqrestore(sch->lock, flags);
 121         return ret;
 122 }
 123 
 124 static int fsm_do_clear(struct vfio_ccw_private *private)
 125 {
 126         struct subchannel *sch;
 127         unsigned long flags;
 128         int ccode;
 129         int ret;
 130 
 131         sch = private->sch;
 132 
 133         spin_lock_irqsave(sch->lock, flags);
 134 
 135         VFIO_CCW_TRACE_EVENT(2, "clearIO");
 136         VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
 137 
 138         /* Issue "Clear Subchannel" */
 139         ccode = csch(sch->schid);
 140 
 141         VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode));
 142 
 143         switch (ccode) {
 144         case 0:
 145                 /*
 146                  * Initialize device status information
 147                  */
 148                 sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND;
 149                 /* TODO: check what else we might need to clear */
 150                 ret = 0;
 151                 break;
 152         case 3:         /* Device not operational */
 153                 ret = -ENODEV;
 154                 break;
 155         default:
 156                 ret = ccode;
 157         }
 158         spin_unlock_irqrestore(sch->lock, flags);
 159         return ret;
 160 }
 161 
 162 static void fsm_notoper(struct vfio_ccw_private *private,
 163                         enum vfio_ccw_event event)
 164 {
 165         struct subchannel *sch = private->sch;
 166 
 167         VFIO_CCW_TRACE_EVENT(2, "notoper");
 168         VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
 169 
 170         /*
 171          * TODO:
 172          * Probably we should send the machine check to the guest.
 173          */
 174         css_sched_sch_todo(sch, SCH_TODO_UNREG);
 175         private->state = VFIO_CCW_STATE_NOT_OPER;
 176 }
 177 
 178 /*
 179  * No operation action.
 180  */
 181 static void fsm_nop(struct vfio_ccw_private *private,
 182                     enum vfio_ccw_event event)
 183 {
 184 }
 185 
 186 static void fsm_io_error(struct vfio_ccw_private *private,
 187                          enum vfio_ccw_event event)
 188 {
 189         pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state);
 190         private->io_region->ret_code = -EIO;
 191 }
 192 
 193 static void fsm_io_busy(struct vfio_ccw_private *private,
 194                         enum vfio_ccw_event event)
 195 {
 196         private->io_region->ret_code = -EBUSY;
 197 }
 198 
 199 static void fsm_io_retry(struct vfio_ccw_private *private,
 200                          enum vfio_ccw_event event)
 201 {
 202         private->io_region->ret_code = -EAGAIN;
 203 }
 204 
 205 static void fsm_async_error(struct vfio_ccw_private *private,
 206                             enum vfio_ccw_event event)
 207 {
 208         struct ccw_cmd_region *cmd_region = private->cmd_region;
 209 
 210         pr_err("vfio-ccw: FSM: %s request from state:%d\n",
 211                cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" :
 212                cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" :
 213                "<unknown>", private->state);
 214         cmd_region->ret_code = -EIO;
 215 }
 216 
 217 static void fsm_async_retry(struct vfio_ccw_private *private,
 218                             enum vfio_ccw_event event)
 219 {
 220         private->cmd_region->ret_code = -EAGAIN;
 221 }
 222 
 223 static void fsm_disabled_irq(struct vfio_ccw_private *private,
 224                              enum vfio_ccw_event event)
 225 {
 226         struct subchannel *sch = private->sch;
 227 
 228         /*
 229          * An interrupt in a disabled state means a previous disable was not
 230          * successful - should not happen, but we try to disable again.
 231          */
 232         cio_disable_subchannel(sch);
 233 }
 234 inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
 235 {
 236         return p->sch->schid;
 237 }
 238 
 239 /*
 240  * Deal with the ccw command request from the userspace.
 241  */
 242 static void fsm_io_request(struct vfio_ccw_private *private,
 243                            enum vfio_ccw_event event)
 244 {
 245         union orb *orb;
 246         union scsw *scsw = &private->scsw;
 247         struct ccw_io_region *io_region = private->io_region;
 248         struct mdev_device *mdev = private->mdev;
 249         char *errstr = "request";
 250         struct subchannel_id schid = get_schid(private);
 251 
 252         private->state = VFIO_CCW_STATE_CP_PROCESSING;
 253         memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
 254 
 255         if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
 256                 orb = (union orb *)io_region->orb_area;
 257 
 258                 /* Don't try to build a cp if transport mode is specified. */
 259                 if (orb->tm.b) {
 260                         io_region->ret_code = -EOPNOTSUPP;
 261                         VFIO_CCW_MSG_EVENT(2,
 262                                            "%pUl (%x.%x.%04x): transport mode\n",
 263                                            mdev_uuid(mdev), schid.cssid,
 264                                            schid.ssid, schid.sch_no);
 265                         errstr = "transport mode";
 266                         goto err_out;
 267                 }
 268                 io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
 269                                               orb);
 270                 if (io_region->ret_code) {
 271                         VFIO_CCW_MSG_EVENT(2,
 272                                            "%pUl (%x.%x.%04x): cp_init=%d\n",
 273                                            mdev_uuid(mdev), schid.cssid,
 274                                            schid.ssid, schid.sch_no,
 275                                            io_region->ret_code);
 276                         errstr = "cp init";
 277                         goto err_out;
 278                 }
 279 
 280                 io_region->ret_code = cp_prefetch(&private->cp);
 281                 if (io_region->ret_code) {
 282                         VFIO_CCW_MSG_EVENT(2,
 283                                            "%pUl (%x.%x.%04x): cp_prefetch=%d\n",
 284                                            mdev_uuid(mdev), schid.cssid,
 285                                            schid.ssid, schid.sch_no,
 286                                            io_region->ret_code);
 287                         errstr = "cp prefetch";
 288                         cp_free(&private->cp);
 289                         goto err_out;
 290                 }
 291 
 292                 /* Start channel program and wait for I/O interrupt. */
 293                 io_region->ret_code = fsm_io_helper(private);
 294                 if (io_region->ret_code) {
 295                         VFIO_CCW_MSG_EVENT(2,
 296                                            "%pUl (%x.%x.%04x): fsm_io_helper=%d\n",
 297                                            mdev_uuid(mdev), schid.cssid,
 298                                            schid.ssid, schid.sch_no,
 299                                            io_region->ret_code);
 300                         errstr = "cp fsm_io_helper";
 301                         cp_free(&private->cp);
 302                         goto err_out;
 303                 }
 304                 return;
 305         } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
 306                 VFIO_CCW_MSG_EVENT(2,
 307                                    "%pUl (%x.%x.%04x): halt on io_region\n",
 308                                    mdev_uuid(mdev), schid.cssid,
 309                                    schid.ssid, schid.sch_no);
 310                 /* halt is handled via the async cmd region */
 311                 io_region->ret_code = -EOPNOTSUPP;
 312                 goto err_out;
 313         } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
 314                 VFIO_CCW_MSG_EVENT(2,
 315                                    "%pUl (%x.%x.%04x): clear on io_region\n",
 316                                    mdev_uuid(mdev), schid.cssid,
 317                                    schid.ssid, schid.sch_no);
 318                 /* clear is handled via the async cmd region */
 319                 io_region->ret_code = -EOPNOTSUPP;
 320                 goto err_out;
 321         }
 322 
 323 err_out:
 324         trace_vfio_ccw_io_fctl(scsw->cmd.fctl, schid,
 325                                io_region->ret_code, errstr);
 326 }
 327 
 328 /*
 329  * Deal with an async request from userspace.
 330  */
 331 static void fsm_async_request(struct vfio_ccw_private *private,
 332                               enum vfio_ccw_event event)
 333 {
 334         struct ccw_cmd_region *cmd_region = private->cmd_region;
 335 
 336         switch (cmd_region->command) {
 337         case VFIO_CCW_ASYNC_CMD_HSCH:
 338                 cmd_region->ret_code = fsm_do_halt(private);
 339                 break;
 340         case VFIO_CCW_ASYNC_CMD_CSCH:
 341                 cmd_region->ret_code = fsm_do_clear(private);
 342                 break;
 343         default:
 344                 /* should not happen? */
 345                 cmd_region->ret_code = -EINVAL;
 346         }
 347 }
 348 
 349 /*
 350  * Got an interrupt for a normal io (state busy).
 351  */
 352 static void fsm_irq(struct vfio_ccw_private *private,
 353                     enum vfio_ccw_event event)
 354 {
 355         struct irb *irb = this_cpu_ptr(&cio_irb);
 356 
 357         VFIO_CCW_TRACE_EVENT(6, "IRQ");
 358         VFIO_CCW_TRACE_EVENT(6, dev_name(&private->sch->dev));
 359 
 360         memcpy(&private->irb, irb, sizeof(*irb));
 361 
 362         queue_work(vfio_ccw_work_q, &private->io_work);
 363 
 364         if (private->completion)
 365                 complete(private->completion);
 366 }
 367 
 368 /*
 369  * Device statemachine
 370  */
 371 fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = {
 372         [VFIO_CCW_STATE_NOT_OPER] = {
 373                 [VFIO_CCW_EVENT_NOT_OPER]       = fsm_nop,
 374                 [VFIO_CCW_EVENT_IO_REQ]         = fsm_io_error,
 375                 [VFIO_CCW_EVENT_ASYNC_REQ]      = fsm_async_error,
 376                 [VFIO_CCW_EVENT_INTERRUPT]      = fsm_disabled_irq,
 377         },
 378         [VFIO_CCW_STATE_STANDBY] = {
 379                 [VFIO_CCW_EVENT_NOT_OPER]       = fsm_notoper,
 380                 [VFIO_CCW_EVENT_IO_REQ]         = fsm_io_error,
 381                 [VFIO_CCW_EVENT_ASYNC_REQ]      = fsm_async_error,
 382                 [VFIO_CCW_EVENT_INTERRUPT]      = fsm_irq,
 383         },
 384         [VFIO_CCW_STATE_IDLE] = {
 385                 [VFIO_CCW_EVENT_NOT_OPER]       = fsm_notoper,
 386                 [VFIO_CCW_EVENT_IO_REQ]         = fsm_io_request,
 387                 [VFIO_CCW_EVENT_ASYNC_REQ]      = fsm_async_request,
 388                 [VFIO_CCW_EVENT_INTERRUPT]      = fsm_irq,
 389         },
 390         [VFIO_CCW_STATE_CP_PROCESSING] = {
 391                 [VFIO_CCW_EVENT_NOT_OPER]       = fsm_notoper,
 392                 [VFIO_CCW_EVENT_IO_REQ]         = fsm_io_retry,
 393                 [VFIO_CCW_EVENT_ASYNC_REQ]      = fsm_async_retry,
 394                 [VFIO_CCW_EVENT_INTERRUPT]      = fsm_irq,
 395         },
 396         [VFIO_CCW_STATE_CP_PENDING] = {
 397                 [VFIO_CCW_EVENT_NOT_OPER]       = fsm_notoper,
 398                 [VFIO_CCW_EVENT_IO_REQ]         = fsm_io_busy,
 399                 [VFIO_CCW_EVENT_ASYNC_REQ]      = fsm_async_request,
 400                 [VFIO_CCW_EVENT_INTERRUPT]      = fsm_irq,
 401         },
 402 };

/* [<][>][^][v][top][bottom][index][help] */