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