1 // SPDX-License-Identifier: GPL-2.0
3 * Finite state machine for vfio-ccw device handling
5 * Copyright IBM Corp. 2017
7 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
10 #include <linux/vfio.h>
11 #include <linux/mdev.h>
14 #include "vfio_ccw_private.h"
16 #define CREATE_TRACE_POINTS
17 #include "vfio_ccw_trace.h"
19 static int fsm_io_helper(struct vfio_ccw_private
*private)
21 struct subchannel
*sch
;
30 spin_lock_irqsave(sch
->lock
, flags
);
31 private->state
= VFIO_CCW_STATE_BUSY
;
33 orb
= cp_get_orb(&private->cp
, (u32
)(addr_t
)sch
, sch
->lpm
);
35 /* Issue "Start Subchannel" */
36 ccode
= ssch(sch
->schid
, orb
);
41 * Initialize device status information
43 sch
->schib
.scsw
.cmd
.actl
|= SCSW_ACTL_START_PEND
;
46 case 1: /* Status pending */
50 case 3: /* Device/path not operational */
58 if (cio_update_schib(sch
))
61 ret
= sch
->lpm
? -EACCES
: -ENODEV
;
67 spin_unlock_irqrestore(sch
->lock
, flags
);
71 static void fsm_notoper(struct vfio_ccw_private
*private,
72 enum vfio_ccw_event event
)
74 struct subchannel
*sch
= private->sch
;
78 * Probably we should send the machine check to the guest.
80 css_sched_sch_todo(sch
, SCH_TODO_UNREG
);
81 private->state
= VFIO_CCW_STATE_NOT_OPER
;
85 * No operation action.
87 static void fsm_nop(struct vfio_ccw_private
*private,
88 enum vfio_ccw_event event
)
92 static void fsm_io_error(struct vfio_ccw_private
*private,
93 enum vfio_ccw_event event
)
95 pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state
);
96 private->io_region
->ret_code
= -EIO
;
99 static void fsm_io_busy(struct vfio_ccw_private
*private,
100 enum vfio_ccw_event event
)
102 private->io_region
->ret_code
= -EBUSY
;
105 static void fsm_disabled_irq(struct vfio_ccw_private
*private,
106 enum vfio_ccw_event event
)
108 struct subchannel
*sch
= private->sch
;
111 * An interrupt in a disabled state means a previous disable was not
112 * successful - should not happen, but we try to disable again.
114 cio_disable_subchannel(sch
);
116 inline struct subchannel_id
get_schid(struct vfio_ccw_private
*p
)
118 return p
->sch
->schid
;
122 * Deal with the ccw command request from the userspace.
124 static void fsm_io_request(struct vfio_ccw_private
*private,
125 enum vfio_ccw_event event
)
128 union scsw
*scsw
= &private->scsw
;
129 struct ccw_io_region
*io_region
= private->io_region
;
130 struct mdev_device
*mdev
= private->mdev
;
131 char *errstr
= "request";
133 private->state
= VFIO_CCW_STATE_BOXED
;
135 memcpy(scsw
, io_region
->scsw_area
, sizeof(*scsw
));
137 if (scsw
->cmd
.fctl
& SCSW_FCTL_START_FUNC
) {
138 orb
= (union orb
*)io_region
->orb_area
;
140 /* Don't try to build a cp if transport mode is specified. */
142 io_region
->ret_code
= -EOPNOTSUPP
;
143 errstr
= "transport mode";
146 io_region
->ret_code
= cp_init(&private->cp
, mdev_dev(mdev
),
148 if (io_region
->ret_code
) {
153 io_region
->ret_code
= cp_prefetch(&private->cp
);
154 if (io_region
->ret_code
) {
155 errstr
= "cp prefetch";
156 cp_free(&private->cp
);
160 /* Start channel program and wait for I/O interrupt. */
161 io_region
->ret_code
= fsm_io_helper(private);
162 if (io_region
->ret_code
) {
163 errstr
= "cp fsm_io_helper";
164 cp_free(&private->cp
);
168 } else if (scsw
->cmd
.fctl
& SCSW_FCTL_HALT_FUNC
) {
169 /* XXX: Handle halt. */
170 io_region
->ret_code
= -EOPNOTSUPP
;
172 } else if (scsw
->cmd
.fctl
& SCSW_FCTL_CLEAR_FUNC
) {
173 /* XXX: Handle clear. */
174 io_region
->ret_code
= -EOPNOTSUPP
;
179 private->state
= VFIO_CCW_STATE_IDLE
;
180 trace_vfio_ccw_io_fctl(scsw
->cmd
.fctl
, get_schid(private),
181 io_region
->ret_code
, errstr
);
185 * Got an interrupt for a normal io (state busy).
187 static void fsm_irq(struct vfio_ccw_private
*private,
188 enum vfio_ccw_event event
)
190 struct irb
*irb
= this_cpu_ptr(&cio_irb
);
192 memcpy(&private->irb
, irb
, sizeof(*irb
));
194 queue_work(vfio_ccw_work_q
, &private->io_work
);
196 if (private->completion
)
197 complete(private->completion
);
201 * Device statemachine
203 fsm_func_t
*vfio_ccw_jumptable
[NR_VFIO_CCW_STATES
][NR_VFIO_CCW_EVENTS
] = {
204 [VFIO_CCW_STATE_NOT_OPER
] = {
205 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_nop
,
206 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_error
,
207 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_disabled_irq
,
209 [VFIO_CCW_STATE_STANDBY
] = {
210 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
211 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_error
,
212 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,
214 [VFIO_CCW_STATE_IDLE
] = {
215 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
216 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_request
,
217 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,
219 [VFIO_CCW_STATE_BOXED
] = {
220 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
221 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_busy
,
222 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,
224 [VFIO_CCW_STATE_BUSY
] = {
225 [VFIO_CCW_EVENT_NOT_OPER
] = fsm_notoper
,
226 [VFIO_CCW_EVENT_IO_REQ
] = fsm_io_busy
,
227 [VFIO_CCW_EVENT_INTERRUPT
] = fsm_irq
,