xref: /openbmc/linux/drivers/s390/cio/vfio_ccw_fsm.c (revision 62ec0d49e683c25e35927a942c64433878de143c)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2bbe37e4cSDong Jia Shi /*
3bbe37e4cSDong Jia Shi  * Finite state machine for vfio-ccw device handling
4bbe37e4cSDong Jia Shi  *
5bbe37e4cSDong Jia Shi  * Copyright IBM Corp. 2017
6d5afd5d1SCornelia Huck  * Copyright Red Hat, Inc. 2019
7bbe37e4cSDong Jia Shi  *
8bbe37e4cSDong Jia Shi  * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
9d5afd5d1SCornelia Huck  *            Cornelia Huck <cohuck@redhat.com>
10bbe37e4cSDong Jia Shi  */
11bbe37e4cSDong Jia Shi 
12bbe37e4cSDong Jia Shi #include <linux/vfio.h>
13bbe37e4cSDong Jia Shi 
14*62ec0d49SEric Farman #include <asm/isc.h>
15*62ec0d49SEric Farman 
16bbe37e4cSDong Jia Shi #include "ioasm.h"
17bbe37e4cSDong Jia Shi #include "vfio_ccw_private.h"
18bbe37e4cSDong Jia Shi 
19bbe37e4cSDong Jia Shi static int fsm_io_helper(struct vfio_ccw_private *private)
20bbe37e4cSDong Jia Shi {
21bbe37e4cSDong Jia Shi 	struct subchannel *sch;
22bbe37e4cSDong Jia Shi 	union orb *orb;
23bbe37e4cSDong Jia Shi 	int ccode;
24bbe37e4cSDong Jia Shi 	__u8 lpm;
25bbe37e4cSDong Jia Shi 	unsigned long flags;
263368e547SCornelia Huck 	int ret;
27bbe37e4cSDong Jia Shi 
28bbe37e4cSDong Jia Shi 	sch = private->sch;
29bbe37e4cSDong Jia Shi 
30bbe37e4cSDong Jia Shi 	spin_lock_irqsave(sch->lock, flags);
31bbe37e4cSDong Jia Shi 
32bbe37e4cSDong Jia Shi 	orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);
3371189f26SCornelia Huck 	if (!orb) {
3471189f26SCornelia Huck 		ret = -EIO;
3571189f26SCornelia Huck 		goto out;
3671189f26SCornelia Huck 	}
37bbe37e4cSDong Jia Shi 
3860e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(5, "stIO");
3960e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(5, dev_name(&sch->dev));
4060e05d1cSCornelia Huck 
41bbe37e4cSDong Jia Shi 	/* Issue "Start Subchannel" */
42bbe37e4cSDong Jia Shi 	ccode = ssch(sch->schid, orb);
43bbe37e4cSDong Jia Shi 
4460e05d1cSCornelia Huck 	VFIO_CCW_HEX_EVENT(5, &ccode, sizeof(ccode));
4560e05d1cSCornelia Huck 
46bbe37e4cSDong Jia Shi 	switch (ccode) {
47bbe37e4cSDong Jia Shi 	case 0:
48bbe37e4cSDong Jia Shi 		/*
49bbe37e4cSDong Jia Shi 		 * Initialize device status information
50bbe37e4cSDong Jia Shi 		 */
51bbe37e4cSDong Jia Shi 		sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
523368e547SCornelia Huck 		ret = 0;
53690f6a15SCornelia Huck 		private->state = VFIO_CCW_STATE_CP_PENDING;
543368e547SCornelia Huck 		break;
55bbe37e4cSDong Jia Shi 	case 1:		/* Status pending */
56bbe37e4cSDong Jia Shi 	case 2:		/* Busy */
573368e547SCornelia Huck 		ret = -EBUSY;
583368e547SCornelia Huck 		break;
59bbe37e4cSDong Jia Shi 	case 3:		/* Device/path not operational */
60bbe37e4cSDong Jia Shi 	{
61bbe37e4cSDong Jia Shi 		lpm = orb->cmd.lpm;
62bbe37e4cSDong Jia Shi 		if (lpm != 0)
63bbe37e4cSDong Jia Shi 			sch->lpm &= ~lpm;
64bbe37e4cSDong Jia Shi 		else
65bbe37e4cSDong Jia Shi 			sch->lpm = 0;
66bbe37e4cSDong Jia Shi 
67bbe37e4cSDong Jia Shi 		if (cio_update_schib(sch))
683368e547SCornelia Huck 			ret = -ENODEV;
693368e547SCornelia Huck 		else
703368e547SCornelia Huck 			ret = sch->lpm ? -EACCES : -ENODEV;
713368e547SCornelia Huck 		break;
72bbe37e4cSDong Jia Shi 	}
73bbe37e4cSDong Jia Shi 	default:
743368e547SCornelia Huck 		ret = ccode;
75bbe37e4cSDong Jia Shi 	}
7671189f26SCornelia Huck out:
773368e547SCornelia Huck 	spin_unlock_irqrestore(sch->lock, flags);
783368e547SCornelia Huck 	return ret;
79bbe37e4cSDong Jia Shi }
80bbe37e4cSDong Jia Shi 
81d5afd5d1SCornelia Huck static int fsm_do_halt(struct vfio_ccw_private *private)
82d5afd5d1SCornelia Huck {
83d5afd5d1SCornelia Huck 	struct subchannel *sch;
84d5afd5d1SCornelia Huck 	unsigned long flags;
85d5afd5d1SCornelia Huck 	int ccode;
86d5afd5d1SCornelia Huck 	int ret;
87d5afd5d1SCornelia Huck 
88d5afd5d1SCornelia Huck 	sch = private->sch;
89d5afd5d1SCornelia Huck 
90d5afd5d1SCornelia Huck 	spin_lock_irqsave(sch->lock, flags);
91d5afd5d1SCornelia Huck 
9260e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(2, "haltIO");
9360e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
9460e05d1cSCornelia Huck 
95d5afd5d1SCornelia Huck 	/* Issue "Halt Subchannel" */
96d5afd5d1SCornelia Huck 	ccode = hsch(sch->schid);
97d5afd5d1SCornelia Huck 
9860e05d1cSCornelia Huck 	VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode));
9960e05d1cSCornelia Huck 
100d5afd5d1SCornelia Huck 	switch (ccode) {
101d5afd5d1SCornelia Huck 	case 0:
102d5afd5d1SCornelia Huck 		/*
103d5afd5d1SCornelia Huck 		 * Initialize device status information
104d5afd5d1SCornelia Huck 		 */
105d5afd5d1SCornelia Huck 		sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
106d5afd5d1SCornelia Huck 		ret = 0;
107d5afd5d1SCornelia Huck 		break;
108d5afd5d1SCornelia Huck 	case 1:		/* Status pending */
109d5afd5d1SCornelia Huck 	case 2:		/* Busy */
110d5afd5d1SCornelia Huck 		ret = -EBUSY;
111d5afd5d1SCornelia Huck 		break;
112d5afd5d1SCornelia Huck 	case 3:		/* Device not operational */
113d5afd5d1SCornelia Huck 		ret = -ENODEV;
114d5afd5d1SCornelia Huck 		break;
115d5afd5d1SCornelia Huck 	default:
116d5afd5d1SCornelia Huck 		ret = ccode;
117d5afd5d1SCornelia Huck 	}
118d5afd5d1SCornelia Huck 	spin_unlock_irqrestore(sch->lock, flags);
119d5afd5d1SCornelia Huck 	return ret;
120d5afd5d1SCornelia Huck }
121d5afd5d1SCornelia Huck 
122d5afd5d1SCornelia Huck static int fsm_do_clear(struct vfio_ccw_private *private)
123d5afd5d1SCornelia Huck {
124d5afd5d1SCornelia Huck 	struct subchannel *sch;
125d5afd5d1SCornelia Huck 	unsigned long flags;
126d5afd5d1SCornelia Huck 	int ccode;
127d5afd5d1SCornelia Huck 	int ret;
128d5afd5d1SCornelia Huck 
129d5afd5d1SCornelia Huck 	sch = private->sch;
130d5afd5d1SCornelia Huck 
131d5afd5d1SCornelia Huck 	spin_lock_irqsave(sch->lock, flags);
132d5afd5d1SCornelia Huck 
13360e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(2, "clearIO");
13460e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev));
13560e05d1cSCornelia Huck 
136d5afd5d1SCornelia Huck 	/* Issue "Clear Subchannel" */
137d5afd5d1SCornelia Huck 	ccode = csch(sch->schid);
138d5afd5d1SCornelia Huck 
13960e05d1cSCornelia Huck 	VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode));
14060e05d1cSCornelia Huck 
141d5afd5d1SCornelia Huck 	switch (ccode) {
142d5afd5d1SCornelia Huck 	case 0:
143d5afd5d1SCornelia Huck 		/*
144d5afd5d1SCornelia Huck 		 * Initialize device status information
145d5afd5d1SCornelia Huck 		 */
146d5afd5d1SCornelia Huck 		sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND;
147d5afd5d1SCornelia Huck 		/* TODO: check what else we might need to clear */
148d5afd5d1SCornelia Huck 		ret = 0;
149d5afd5d1SCornelia Huck 		break;
150d5afd5d1SCornelia Huck 	case 3:		/* Device not operational */
151d5afd5d1SCornelia Huck 		ret = -ENODEV;
152d5afd5d1SCornelia Huck 		break;
153d5afd5d1SCornelia Huck 	default:
154d5afd5d1SCornelia Huck 		ret = ccode;
155d5afd5d1SCornelia Huck 	}
156d5afd5d1SCornelia Huck 	spin_unlock_irqrestore(sch->lock, flags);
157d5afd5d1SCornelia Huck 	return ret;
158d5afd5d1SCornelia Huck }
159d5afd5d1SCornelia Huck 
160bbe37e4cSDong Jia Shi static void fsm_notoper(struct vfio_ccw_private *private,
161bbe37e4cSDong Jia Shi 			enum vfio_ccw_event event)
162bbe37e4cSDong Jia Shi {
163bbe37e4cSDong Jia Shi 	struct subchannel *sch = private->sch;
164bbe37e4cSDong Jia Shi 
1654cc2c051SEric Farman 	VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: notoper event %x state %x\n",
1664cc2c051SEric Farman 			   sch->schid.cssid,
1674cc2c051SEric Farman 			   sch->schid.ssid,
1684cc2c051SEric Farman 			   sch->schid.sch_no,
1694cc2c051SEric Farman 			   event,
1704cc2c051SEric Farman 			   private->state);
17160e05d1cSCornelia Huck 
172bbe37e4cSDong Jia Shi 	/*
173bbe37e4cSDong Jia Shi 	 * TODO:
174bbe37e4cSDong Jia Shi 	 * Probably we should send the machine check to the guest.
175bbe37e4cSDong Jia Shi 	 */
176bbe37e4cSDong Jia Shi 	css_sched_sch_todo(sch, SCH_TODO_UNREG);
177bbe37e4cSDong Jia Shi 	private->state = VFIO_CCW_STATE_NOT_OPER;
178bbe37e4cSDong Jia Shi }
179bbe37e4cSDong Jia Shi 
180bbe37e4cSDong Jia Shi /*
181bbe37e4cSDong Jia Shi  * No operation action.
182bbe37e4cSDong Jia Shi  */
183bbe37e4cSDong Jia Shi static void fsm_nop(struct vfio_ccw_private *private,
184bbe37e4cSDong Jia Shi 		    enum vfio_ccw_event event)
185bbe37e4cSDong Jia Shi {
186bbe37e4cSDong Jia Shi }
187bbe37e4cSDong Jia Shi 
188bbe37e4cSDong Jia Shi static void fsm_io_error(struct vfio_ccw_private *private,
189bbe37e4cSDong Jia Shi 			 enum vfio_ccw_event event)
190bbe37e4cSDong Jia Shi {
191bbe37e4cSDong Jia Shi 	pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state);
192c98e16b2SEric Farman 	private->io_region->ret_code = -EIO;
193bbe37e4cSDong Jia Shi }
194bbe37e4cSDong Jia Shi 
195bbe37e4cSDong Jia Shi static void fsm_io_busy(struct vfio_ccw_private *private,
196bbe37e4cSDong Jia Shi 			enum vfio_ccw_event event)
197bbe37e4cSDong Jia Shi {
198c98e16b2SEric Farman 	private->io_region->ret_code = -EBUSY;
199bbe37e4cSDong Jia Shi }
200bbe37e4cSDong Jia Shi 
201690f6a15SCornelia Huck static void fsm_io_retry(struct vfio_ccw_private *private,
202690f6a15SCornelia Huck 			 enum vfio_ccw_event event)
203690f6a15SCornelia Huck {
204690f6a15SCornelia Huck 	private->io_region->ret_code = -EAGAIN;
205690f6a15SCornelia Huck }
206690f6a15SCornelia Huck 
207d5afd5d1SCornelia Huck static void fsm_async_error(struct vfio_ccw_private *private,
208d5afd5d1SCornelia Huck 			    enum vfio_ccw_event event)
209d5afd5d1SCornelia Huck {
210d5afd5d1SCornelia Huck 	struct ccw_cmd_region *cmd_region = private->cmd_region;
211d5afd5d1SCornelia Huck 
212d5afd5d1SCornelia Huck 	pr_err("vfio-ccw: FSM: %s request from state:%d\n",
213d5afd5d1SCornelia Huck 	       cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" :
214d5afd5d1SCornelia Huck 	       cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" :
215d5afd5d1SCornelia Huck 	       "<unknown>", private->state);
216d5afd5d1SCornelia Huck 	cmd_region->ret_code = -EIO;
217d5afd5d1SCornelia Huck }
218d5afd5d1SCornelia Huck 
219d5afd5d1SCornelia Huck static void fsm_async_retry(struct vfio_ccw_private *private,
220d5afd5d1SCornelia Huck 			    enum vfio_ccw_event event)
221d5afd5d1SCornelia Huck {
222d5afd5d1SCornelia Huck 	private->cmd_region->ret_code = -EAGAIN;
223d5afd5d1SCornelia Huck }
224d5afd5d1SCornelia Huck 
225bbe37e4cSDong Jia Shi static void fsm_disabled_irq(struct vfio_ccw_private *private,
226bbe37e4cSDong Jia Shi 			     enum vfio_ccw_event event)
227bbe37e4cSDong Jia Shi {
228bbe37e4cSDong Jia Shi 	struct subchannel *sch = private->sch;
229bbe37e4cSDong Jia Shi 
230bbe37e4cSDong Jia Shi 	/*
231bbe37e4cSDong Jia Shi 	 * An interrupt in a disabled state means a previous disable was not
232bbe37e4cSDong Jia Shi 	 * successful - should not happen, but we try to disable again.
233bbe37e4cSDong Jia Shi 	 */
234bbe37e4cSDong Jia Shi 	cio_disable_subchannel(sch);
235bbe37e4cSDong Jia Shi }
2363cd90214SHalil Pasic inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
2373cd90214SHalil Pasic {
2383cd90214SHalil Pasic 	return p->sch->schid;
2393cd90214SHalil Pasic }
240bbe37e4cSDong Jia Shi 
241bbe37e4cSDong Jia Shi /*
242bbe37e4cSDong Jia Shi  * Deal with the ccw command request from the userspace.
243bbe37e4cSDong Jia Shi  */
244bbe37e4cSDong Jia Shi static void fsm_io_request(struct vfio_ccw_private *private,
245bbe37e4cSDong Jia Shi 			   enum vfio_ccw_event event)
246bbe37e4cSDong Jia Shi {
247bbe37e4cSDong Jia Shi 	union orb *orb;
248bbe37e4cSDong Jia Shi 	union scsw *scsw = &private->scsw;
249c98e16b2SEric Farman 	struct ccw_io_region *io_region = private->io_region;
2503cd90214SHalil Pasic 	char *errstr = "request";
25160e05d1cSCornelia Huck 	struct subchannel_id schid = get_schid(private);
252bbe37e4cSDong Jia Shi 
253690f6a15SCornelia Huck 	private->state = VFIO_CCW_STATE_CP_PROCESSING;
254bbe37e4cSDong Jia Shi 	memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
255bbe37e4cSDong Jia Shi 
256bbe37e4cSDong Jia Shi 	if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
257bbe37e4cSDong Jia Shi 		orb = (union orb *)io_region->orb_area;
258bbe37e4cSDong Jia Shi 
2599851bc77SCornelia Huck 		/* Don't try to build a cp if transport mode is specified. */
2609851bc77SCornelia Huck 		if (orb->tm.b) {
2619851bc77SCornelia Huck 			io_region->ret_code = -EOPNOTSUPP;
26260e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2633566ee1dSMichael Kawano 					   "sch %x.%x.%04x: transport mode\n",
2643566ee1dSMichael Kawano 					   schid.cssid,
26560e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no);
2663cd90214SHalil Pasic 			errstr = "transport mode";
2679851bc77SCornelia Huck 			goto err_out;
2689851bc77SCornelia Huck 		}
2690a587956SJason Gunthorpe 		io_region->ret_code = cp_init(&private->cp, orb);
2703cd90214SHalil Pasic 		if (io_region->ret_code) {
27160e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2723566ee1dSMichael Kawano 					   "sch %x.%x.%04x: cp_init=%d\n",
2733566ee1dSMichael Kawano 					   schid.cssid,
27460e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no,
27560e05d1cSCornelia Huck 					   io_region->ret_code);
2763cd90214SHalil Pasic 			errstr = "cp init";
277bbe37e4cSDong Jia Shi 			goto err_out;
2783cd90214SHalil Pasic 		}
279bbe37e4cSDong Jia Shi 
280bbe37e4cSDong Jia Shi 		io_region->ret_code = cp_prefetch(&private->cp);
281bbe37e4cSDong Jia Shi 		if (io_region->ret_code) {
28260e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2833566ee1dSMichael Kawano 					   "sch %x.%x.%04x: cp_prefetch=%d\n",
2843566ee1dSMichael Kawano 					   schid.cssid,
28560e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no,
28660e05d1cSCornelia Huck 					   io_region->ret_code);
2873cd90214SHalil Pasic 			errstr = "cp prefetch";
288bbe37e4cSDong Jia Shi 			cp_free(&private->cp);
289bbe37e4cSDong Jia Shi 			goto err_out;
290bbe37e4cSDong Jia Shi 		}
291bbe37e4cSDong Jia Shi 
292bbe37e4cSDong Jia Shi 		/* Start channel program and wait for I/O interrupt. */
293bbe37e4cSDong Jia Shi 		io_region->ret_code = fsm_io_helper(private);
294bbe37e4cSDong Jia Shi 		if (io_region->ret_code) {
29560e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2963566ee1dSMichael Kawano 					   "sch %x.%x.%04x: fsm_io_helper=%d\n",
2973566ee1dSMichael Kawano 					   schid.cssid,
29860e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no,
29960e05d1cSCornelia Huck 					   io_region->ret_code);
3003cd90214SHalil Pasic 			errstr = "cp fsm_io_helper";
301bbe37e4cSDong Jia Shi 			cp_free(&private->cp);
302bbe37e4cSDong Jia Shi 			goto err_out;
303bbe37e4cSDong Jia Shi 		}
304bbe37e4cSDong Jia Shi 		return;
305bbe37e4cSDong Jia Shi 	} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
30660e05d1cSCornelia Huck 		VFIO_CCW_MSG_EVENT(2,
3073566ee1dSMichael Kawano 				   "sch %x.%x.%04x: halt on io_region\n",
3083566ee1dSMichael Kawano 				   schid.cssid,
30960e05d1cSCornelia Huck 				   schid.ssid, schid.sch_no);
310d5afd5d1SCornelia Huck 		/* halt is handled via the async cmd region */
311bbe37e4cSDong Jia Shi 		io_region->ret_code = -EOPNOTSUPP;
312bbe37e4cSDong Jia Shi 		goto err_out;
313bbe37e4cSDong Jia Shi 	} else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
31460e05d1cSCornelia Huck 		VFIO_CCW_MSG_EVENT(2,
3153566ee1dSMichael Kawano 				   "sch %x.%x.%04x: clear on io_region\n",
3163566ee1dSMichael Kawano 				   schid.cssid,
31760e05d1cSCornelia Huck 				   schid.ssid, schid.sch_no);
318d5afd5d1SCornelia Huck 		/* clear is handled via the async cmd region */
319bbe37e4cSDong Jia Shi 		io_region->ret_code = -EOPNOTSUPP;
320bbe37e4cSDong Jia Shi 		goto err_out;
321bbe37e4cSDong Jia Shi 	}
322bbe37e4cSDong Jia Shi 
323bbe37e4cSDong Jia Shi err_out:
3246c02ac4cSEric Farman 	private->state = VFIO_CCW_STATE_IDLE;
32585298880SEric Farman 	trace_vfio_ccw_fsm_io_request(scsw->cmd.fctl, schid,
3263cd90214SHalil Pasic 				      io_region->ret_code, errstr);
327bbe37e4cSDong Jia Shi }
328bbe37e4cSDong Jia Shi 
329bbe37e4cSDong Jia Shi /*
330d5afd5d1SCornelia Huck  * Deal with an async request from userspace.
331d5afd5d1SCornelia Huck  */
332d5afd5d1SCornelia Huck static void fsm_async_request(struct vfio_ccw_private *private,
333d5afd5d1SCornelia Huck 			      enum vfio_ccw_event event)
334d5afd5d1SCornelia Huck {
335d5afd5d1SCornelia Huck 	struct ccw_cmd_region *cmd_region = private->cmd_region;
336d5afd5d1SCornelia Huck 
337d5afd5d1SCornelia Huck 	switch (cmd_region->command) {
338d5afd5d1SCornelia Huck 	case VFIO_CCW_ASYNC_CMD_HSCH:
339d5afd5d1SCornelia Huck 		cmd_region->ret_code = fsm_do_halt(private);
340d5afd5d1SCornelia Huck 		break;
341d5afd5d1SCornelia Huck 	case VFIO_CCW_ASYNC_CMD_CSCH:
342d5afd5d1SCornelia Huck 		cmd_region->ret_code = fsm_do_clear(private);
343d5afd5d1SCornelia Huck 		break;
344d5afd5d1SCornelia Huck 	default:
345d5afd5d1SCornelia Huck 		/* should not happen? */
346d5afd5d1SCornelia Huck 		cmd_region->ret_code = -EINVAL;
347d5afd5d1SCornelia Huck 	}
348d5950b02SEric Farman 
349d5950b02SEric Farman 	trace_vfio_ccw_fsm_async_request(get_schid(private),
350d5950b02SEric Farman 					 cmd_region->command,
351d5950b02SEric Farman 					 cmd_region->ret_code);
352d5afd5d1SCornelia Huck }
353d5afd5d1SCornelia Huck 
354d5afd5d1SCornelia Huck /*
355bbe37e4cSDong Jia Shi  * Got an interrupt for a normal io (state busy).
356bbe37e4cSDong Jia Shi  */
357bbe37e4cSDong Jia Shi static void fsm_irq(struct vfio_ccw_private *private,
358bbe37e4cSDong Jia Shi 		    enum vfio_ccw_event event)
359bbe37e4cSDong Jia Shi {
360c9c31b07SDong Jia Shi 	struct irb *irb = this_cpu_ptr(&cio_irb);
361bbe37e4cSDong Jia Shi 
36260e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(6, "IRQ");
36360e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(6, dev_name(&private->sch->dev));
36460e05d1cSCornelia Huck 
365bbe37e4cSDong Jia Shi 	memcpy(&private->irb, irb, sizeof(*irb));
366bbe37e4cSDong Jia Shi 
367bbe37e4cSDong Jia Shi 	queue_work(vfio_ccw_work_q, &private->io_work);
368bbe37e4cSDong Jia Shi 
369bbe37e4cSDong Jia Shi 	if (private->completion)
370bbe37e4cSDong Jia Shi 		complete(private->completion);
371bbe37e4cSDong Jia Shi }
372bbe37e4cSDong Jia Shi 
373*62ec0d49SEric Farman static void fsm_open(struct vfio_ccw_private *private,
374*62ec0d49SEric Farman 		     enum vfio_ccw_event event)
375*62ec0d49SEric Farman {
376*62ec0d49SEric Farman 	struct subchannel *sch = private->sch;
377*62ec0d49SEric Farman 	int ret;
378*62ec0d49SEric Farman 
379*62ec0d49SEric Farman 	spin_lock_irq(sch->lock);
380*62ec0d49SEric Farman 	sch->isc = VFIO_CCW_ISC;
381*62ec0d49SEric Farman 	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
382*62ec0d49SEric Farman 	if (!ret)
383*62ec0d49SEric Farman 		private->state = VFIO_CCW_STATE_STANDBY;
384*62ec0d49SEric Farman 	spin_unlock_irq(sch->lock);
385*62ec0d49SEric Farman }
386*62ec0d49SEric Farman 
387bbe37e4cSDong Jia Shi /*
388bbe37e4cSDong Jia Shi  * Device statemachine
389bbe37e4cSDong Jia Shi  */
390bbe37e4cSDong Jia Shi fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = {
391bbe37e4cSDong Jia Shi 	[VFIO_CCW_STATE_NOT_OPER] = {
392bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_nop,
393bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_error,
394d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_error,
395bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_disabled_irq,
396*62ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_open,
397bbe37e4cSDong Jia Shi 	},
398bbe37e4cSDong Jia Shi 	[VFIO_CCW_STATE_STANDBY] = {
399bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
400bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_error,
401d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_error,
402bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
403*62ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
404bbe37e4cSDong Jia Shi 	},
405bbe37e4cSDong Jia Shi 	[VFIO_CCW_STATE_IDLE] = {
406bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
407bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_request,
408d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_request,
409bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
410*62ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
411bbe37e4cSDong Jia Shi 	},
412690f6a15SCornelia Huck 	[VFIO_CCW_STATE_CP_PROCESSING] = {
413690f6a15SCornelia Huck 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
414690f6a15SCornelia Huck 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_retry,
415d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_retry,
416690f6a15SCornelia Huck 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
417*62ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
418690f6a15SCornelia Huck 	},
419690f6a15SCornelia Huck 	[VFIO_CCW_STATE_CP_PENDING] = {
420bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
421bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_busy,
422d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_request,
423bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
424*62ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
425bbe37e4cSDong Jia Shi 	},
426bbe37e4cSDong Jia Shi };
427