xref: /openbmc/linux/drivers/s390/cio/vfio_ccw_fsm.c (revision 4eb919663d97e056dff8963fde22df3b0ad4d02b)
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 
1462ec0d49SEric Farman #include <asm/isc.h>
1562ec0d49SEric 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;
178204b394aSEric Farman 
179204b394aSEric Farman 	/* This is usually handled during CLOSE event */
180204b394aSEric Farman 	cp_free(&private->cp);
181bbe37e4cSDong Jia Shi }
182bbe37e4cSDong Jia Shi 
183bbe37e4cSDong Jia Shi /*
184bbe37e4cSDong Jia Shi  * No operation action.
185bbe37e4cSDong Jia Shi  */
186bbe37e4cSDong Jia Shi static void fsm_nop(struct vfio_ccw_private *private,
187bbe37e4cSDong Jia Shi 		    enum vfio_ccw_event event)
188bbe37e4cSDong Jia Shi {
189bbe37e4cSDong Jia Shi }
190bbe37e4cSDong Jia Shi 
191bbe37e4cSDong Jia Shi static void fsm_io_error(struct vfio_ccw_private *private,
192bbe37e4cSDong Jia Shi 			 enum vfio_ccw_event event)
193bbe37e4cSDong Jia Shi {
194bbe37e4cSDong Jia Shi 	pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state);
195c98e16b2SEric Farman 	private->io_region->ret_code = -EIO;
196bbe37e4cSDong Jia Shi }
197bbe37e4cSDong Jia Shi 
198bbe37e4cSDong Jia Shi static void fsm_io_busy(struct vfio_ccw_private *private,
199bbe37e4cSDong Jia Shi 			enum vfio_ccw_event event)
200bbe37e4cSDong Jia Shi {
201c98e16b2SEric Farman 	private->io_region->ret_code = -EBUSY;
202bbe37e4cSDong Jia Shi }
203bbe37e4cSDong Jia Shi 
204690f6a15SCornelia Huck static void fsm_io_retry(struct vfio_ccw_private *private,
205690f6a15SCornelia Huck 			 enum vfio_ccw_event event)
206690f6a15SCornelia Huck {
207690f6a15SCornelia Huck 	private->io_region->ret_code = -EAGAIN;
208690f6a15SCornelia Huck }
209690f6a15SCornelia Huck 
210d5afd5d1SCornelia Huck static void fsm_async_error(struct vfio_ccw_private *private,
211d5afd5d1SCornelia Huck 			    enum vfio_ccw_event event)
212d5afd5d1SCornelia Huck {
213d5afd5d1SCornelia Huck 	struct ccw_cmd_region *cmd_region = private->cmd_region;
214d5afd5d1SCornelia Huck 
215d5afd5d1SCornelia Huck 	pr_err("vfio-ccw: FSM: %s request from state:%d\n",
216d5afd5d1SCornelia Huck 	       cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" :
217d5afd5d1SCornelia Huck 	       cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" :
218d5afd5d1SCornelia Huck 	       "<unknown>", private->state);
219d5afd5d1SCornelia Huck 	cmd_region->ret_code = -EIO;
220d5afd5d1SCornelia Huck }
221d5afd5d1SCornelia Huck 
222d5afd5d1SCornelia Huck static void fsm_async_retry(struct vfio_ccw_private *private,
223d5afd5d1SCornelia Huck 			    enum vfio_ccw_event event)
224d5afd5d1SCornelia Huck {
225d5afd5d1SCornelia Huck 	private->cmd_region->ret_code = -EAGAIN;
226d5afd5d1SCornelia Huck }
227d5afd5d1SCornelia Huck 
228bbe37e4cSDong Jia Shi static void fsm_disabled_irq(struct vfio_ccw_private *private,
229bbe37e4cSDong Jia Shi 			     enum vfio_ccw_event event)
230bbe37e4cSDong Jia Shi {
231bbe37e4cSDong Jia Shi 	struct subchannel *sch = private->sch;
232bbe37e4cSDong Jia Shi 
233bbe37e4cSDong Jia Shi 	/*
234bbe37e4cSDong Jia Shi 	 * An interrupt in a disabled state means a previous disable was not
235bbe37e4cSDong Jia Shi 	 * successful - should not happen, but we try to disable again.
236bbe37e4cSDong Jia Shi 	 */
237bbe37e4cSDong Jia Shi 	cio_disable_subchannel(sch);
238bbe37e4cSDong Jia Shi }
2393cd90214SHalil Pasic inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
2403cd90214SHalil Pasic {
2413cd90214SHalil Pasic 	return p->sch->schid;
2423cd90214SHalil Pasic }
243bbe37e4cSDong Jia Shi 
244bbe37e4cSDong Jia Shi /*
245bbe37e4cSDong Jia Shi  * Deal with the ccw command request from the userspace.
246bbe37e4cSDong Jia Shi  */
247bbe37e4cSDong Jia Shi static void fsm_io_request(struct vfio_ccw_private *private,
248bbe37e4cSDong Jia Shi 			   enum vfio_ccw_event event)
249bbe37e4cSDong Jia Shi {
250bbe37e4cSDong Jia Shi 	union orb *orb;
251bbe37e4cSDong Jia Shi 	union scsw *scsw = &private->scsw;
252c98e16b2SEric Farman 	struct ccw_io_region *io_region = private->io_region;
2533cd90214SHalil Pasic 	char *errstr = "request";
25460e05d1cSCornelia Huck 	struct subchannel_id schid = get_schid(private);
255bbe37e4cSDong Jia Shi 
256690f6a15SCornelia Huck 	private->state = VFIO_CCW_STATE_CP_PROCESSING;
257bbe37e4cSDong Jia Shi 	memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
258bbe37e4cSDong Jia Shi 
259bbe37e4cSDong Jia Shi 	if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
260bbe37e4cSDong Jia Shi 		orb = (union orb *)io_region->orb_area;
261bbe37e4cSDong Jia Shi 
2629851bc77SCornelia Huck 		/* Don't try to build a cp if transport mode is specified. */
2639851bc77SCornelia Huck 		if (orb->tm.b) {
2649851bc77SCornelia Huck 			io_region->ret_code = -EOPNOTSUPP;
26560e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2663566ee1dSMichael Kawano 					   "sch %x.%x.%04x: transport mode\n",
2673566ee1dSMichael Kawano 					   schid.cssid,
26860e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no);
2693cd90214SHalil Pasic 			errstr = "transport mode";
2709851bc77SCornelia Huck 			goto err_out;
2719851bc77SCornelia Huck 		}
2720a587956SJason Gunthorpe 		io_region->ret_code = cp_init(&private->cp, orb);
2733cd90214SHalil Pasic 		if (io_region->ret_code) {
27460e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2753566ee1dSMichael Kawano 					   "sch %x.%x.%04x: cp_init=%d\n",
2763566ee1dSMichael Kawano 					   schid.cssid,
27760e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no,
27860e05d1cSCornelia Huck 					   io_region->ret_code);
2793cd90214SHalil Pasic 			errstr = "cp init";
280bbe37e4cSDong Jia Shi 			goto err_out;
2813cd90214SHalil Pasic 		}
282bbe37e4cSDong Jia Shi 
283bbe37e4cSDong Jia Shi 		io_region->ret_code = cp_prefetch(&private->cp);
284bbe37e4cSDong Jia Shi 		if (io_region->ret_code) {
28560e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2863566ee1dSMichael Kawano 					   "sch %x.%x.%04x: cp_prefetch=%d\n",
2873566ee1dSMichael Kawano 					   schid.cssid,
28860e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no,
28960e05d1cSCornelia Huck 					   io_region->ret_code);
2903cd90214SHalil Pasic 			errstr = "cp prefetch";
291bbe37e4cSDong Jia Shi 			cp_free(&private->cp);
292bbe37e4cSDong Jia Shi 			goto err_out;
293bbe37e4cSDong Jia Shi 		}
294bbe37e4cSDong Jia Shi 
295bbe37e4cSDong Jia Shi 		/* Start channel program and wait for I/O interrupt. */
296bbe37e4cSDong Jia Shi 		io_region->ret_code = fsm_io_helper(private);
297bbe37e4cSDong Jia Shi 		if (io_region->ret_code) {
29860e05d1cSCornelia Huck 			VFIO_CCW_MSG_EVENT(2,
2993566ee1dSMichael Kawano 					   "sch %x.%x.%04x: fsm_io_helper=%d\n",
3003566ee1dSMichael Kawano 					   schid.cssid,
30160e05d1cSCornelia Huck 					   schid.ssid, schid.sch_no,
30260e05d1cSCornelia Huck 					   io_region->ret_code);
3033cd90214SHalil Pasic 			errstr = "cp fsm_io_helper";
304bbe37e4cSDong Jia Shi 			cp_free(&private->cp);
305bbe37e4cSDong Jia Shi 			goto err_out;
306bbe37e4cSDong Jia Shi 		}
307bbe37e4cSDong Jia Shi 		return;
308bbe37e4cSDong Jia Shi 	} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
30960e05d1cSCornelia Huck 		VFIO_CCW_MSG_EVENT(2,
3103566ee1dSMichael Kawano 				   "sch %x.%x.%04x: halt on io_region\n",
3113566ee1dSMichael Kawano 				   schid.cssid,
31260e05d1cSCornelia Huck 				   schid.ssid, schid.sch_no);
313d5afd5d1SCornelia Huck 		/* halt is handled via the async cmd region */
314bbe37e4cSDong Jia Shi 		io_region->ret_code = -EOPNOTSUPP;
315bbe37e4cSDong Jia Shi 		goto err_out;
316bbe37e4cSDong Jia Shi 	} else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
31760e05d1cSCornelia Huck 		VFIO_CCW_MSG_EVENT(2,
3183566ee1dSMichael Kawano 				   "sch %x.%x.%04x: clear on io_region\n",
3193566ee1dSMichael Kawano 				   schid.cssid,
32060e05d1cSCornelia Huck 				   schid.ssid, schid.sch_no);
321d5afd5d1SCornelia Huck 		/* clear is handled via the async cmd region */
322bbe37e4cSDong Jia Shi 		io_region->ret_code = -EOPNOTSUPP;
323bbe37e4cSDong Jia Shi 		goto err_out;
324bbe37e4cSDong Jia Shi 	}
325bbe37e4cSDong Jia Shi 
326bbe37e4cSDong Jia Shi err_out:
3276c02ac4cSEric Farman 	private->state = VFIO_CCW_STATE_IDLE;
32885298880SEric Farman 	trace_vfio_ccw_fsm_io_request(scsw->cmd.fctl, schid,
3293cd90214SHalil Pasic 				      io_region->ret_code, errstr);
330bbe37e4cSDong Jia Shi }
331bbe37e4cSDong Jia Shi 
332bbe37e4cSDong Jia Shi /*
333d5afd5d1SCornelia Huck  * Deal with an async request from userspace.
334d5afd5d1SCornelia Huck  */
335d5afd5d1SCornelia Huck static void fsm_async_request(struct vfio_ccw_private *private,
336d5afd5d1SCornelia Huck 			      enum vfio_ccw_event event)
337d5afd5d1SCornelia Huck {
338d5afd5d1SCornelia Huck 	struct ccw_cmd_region *cmd_region = private->cmd_region;
339d5afd5d1SCornelia Huck 
340d5afd5d1SCornelia Huck 	switch (cmd_region->command) {
341d5afd5d1SCornelia Huck 	case VFIO_CCW_ASYNC_CMD_HSCH:
342d5afd5d1SCornelia Huck 		cmd_region->ret_code = fsm_do_halt(private);
343d5afd5d1SCornelia Huck 		break;
344d5afd5d1SCornelia Huck 	case VFIO_CCW_ASYNC_CMD_CSCH:
345d5afd5d1SCornelia Huck 		cmd_region->ret_code = fsm_do_clear(private);
346d5afd5d1SCornelia Huck 		break;
347d5afd5d1SCornelia Huck 	default:
348d5afd5d1SCornelia Huck 		/* should not happen? */
349d5afd5d1SCornelia Huck 		cmd_region->ret_code = -EINVAL;
350d5afd5d1SCornelia Huck 	}
351d5950b02SEric Farman 
352d5950b02SEric Farman 	trace_vfio_ccw_fsm_async_request(get_schid(private),
353d5950b02SEric Farman 					 cmd_region->command,
354d5950b02SEric Farman 					 cmd_region->ret_code);
355d5afd5d1SCornelia Huck }
356d5afd5d1SCornelia Huck 
357d5afd5d1SCornelia Huck /*
358bbe37e4cSDong Jia Shi  * Got an interrupt for a normal io (state busy).
359bbe37e4cSDong Jia Shi  */
360bbe37e4cSDong Jia Shi static void fsm_irq(struct vfio_ccw_private *private,
361bbe37e4cSDong Jia Shi 		    enum vfio_ccw_event event)
362bbe37e4cSDong Jia Shi {
363c9c31b07SDong Jia Shi 	struct irb *irb = this_cpu_ptr(&cio_irb);
364bbe37e4cSDong Jia Shi 
36560e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(6, "IRQ");
36660e05d1cSCornelia Huck 	VFIO_CCW_TRACE_EVENT(6, dev_name(&private->sch->dev));
36760e05d1cSCornelia Huck 
368bbe37e4cSDong Jia Shi 	memcpy(&private->irb, irb, sizeof(*irb));
369bbe37e4cSDong Jia Shi 
370bbe37e4cSDong Jia Shi 	queue_work(vfio_ccw_work_q, &private->io_work);
371bbe37e4cSDong Jia Shi 
372bbe37e4cSDong Jia Shi 	if (private->completion)
373bbe37e4cSDong Jia Shi 		complete(private->completion);
374bbe37e4cSDong Jia Shi }
375bbe37e4cSDong Jia Shi 
37662ec0d49SEric Farman static void fsm_open(struct vfio_ccw_private *private,
37762ec0d49SEric Farman 		     enum vfio_ccw_event event)
37862ec0d49SEric Farman {
37962ec0d49SEric Farman 	struct subchannel *sch = private->sch;
38062ec0d49SEric Farman 	int ret;
38162ec0d49SEric Farman 
38262ec0d49SEric Farman 	spin_lock_irq(sch->lock);
38362ec0d49SEric Farman 	sch->isc = VFIO_CCW_ISC;
38462ec0d49SEric Farman 	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
385204b394aSEric Farman 	if (ret)
386204b394aSEric Farman 		goto err_unlock;
387204b394aSEric Farman 
388204b394aSEric Farman 	private->state = VFIO_CCW_STATE_IDLE;
38962ec0d49SEric Farman 	spin_unlock_irq(sch->lock);
390204b394aSEric Farman 	return;
391204b394aSEric Farman 
392204b394aSEric Farman err_unlock:
393204b394aSEric Farman 	spin_unlock_irq(sch->lock);
394204b394aSEric Farman 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
39562ec0d49SEric Farman }
39662ec0d49SEric Farman 
397f4b4ed44SEric Farman static void fsm_close(struct vfio_ccw_private *private,
398f4b4ed44SEric Farman 		      enum vfio_ccw_event event)
399f4b4ed44SEric Farman {
400f4b4ed44SEric Farman 	struct subchannel *sch = private->sch;
401f4b4ed44SEric Farman 	int ret;
402f4b4ed44SEric Farman 
403f4b4ed44SEric Farman 	spin_lock_irq(sch->lock);
404f4b4ed44SEric Farman 
405f4b4ed44SEric Farman 	if (!sch->schib.pmcw.ena)
406204b394aSEric Farman 		goto err_unlock;
407f4b4ed44SEric Farman 
408f4b4ed44SEric Farman 	ret = cio_disable_subchannel(sch);
409f4b4ed44SEric Farman 	if (ret == -EBUSY)
410*4eb91966SEric Farman 		ret = vfio_ccw_sch_quiesce(sch);
411204b394aSEric Farman 	if (ret)
412204b394aSEric Farman 		goto err_unlock;
413f4b4ed44SEric Farman 
414204b394aSEric Farman 	private->state = VFIO_CCW_STATE_STANDBY;
415f4b4ed44SEric Farman 	spin_unlock_irq(sch->lock);
416f4b4ed44SEric Farman 	cp_free(&private->cp);
417204b394aSEric Farman 	return;
418204b394aSEric Farman 
419204b394aSEric Farman err_unlock:
420204b394aSEric Farman 	spin_unlock_irq(sch->lock);
421204b394aSEric Farman 	vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
422f4b4ed44SEric Farman }
423f4b4ed44SEric Farman 
424bbe37e4cSDong Jia Shi /*
425bbe37e4cSDong Jia Shi  * Device statemachine
426bbe37e4cSDong Jia Shi  */
427bbe37e4cSDong Jia Shi fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = {
428bbe37e4cSDong Jia Shi 	[VFIO_CCW_STATE_NOT_OPER] = {
429bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_nop,
430bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_error,
431d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_error,
432bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_disabled_irq,
433204b394aSEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_nop,
434f4b4ed44SEric Farman 		[VFIO_CCW_EVENT_CLOSE]		= fsm_nop,
435bbe37e4cSDong Jia Shi 	},
436bbe37e4cSDong Jia Shi 	[VFIO_CCW_STATE_STANDBY] = {
437bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
438bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_error,
439d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_error,
440204b394aSEric Farman 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_disabled_irq,
441204b394aSEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_open,
442204b394aSEric Farman 		[VFIO_CCW_EVENT_CLOSE]		= fsm_notoper,
443bbe37e4cSDong Jia Shi 	},
444bbe37e4cSDong Jia Shi 	[VFIO_CCW_STATE_IDLE] = {
445bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
446bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_request,
447d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_request,
448bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
44962ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
450f4b4ed44SEric Farman 		[VFIO_CCW_EVENT_CLOSE]		= fsm_close,
451bbe37e4cSDong Jia Shi 	},
452690f6a15SCornelia Huck 	[VFIO_CCW_STATE_CP_PROCESSING] = {
453690f6a15SCornelia Huck 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
454690f6a15SCornelia Huck 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_retry,
455d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_retry,
456690f6a15SCornelia Huck 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
45762ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
458f4b4ed44SEric Farman 		[VFIO_CCW_EVENT_CLOSE]		= fsm_close,
459690f6a15SCornelia Huck 	},
460690f6a15SCornelia Huck 	[VFIO_CCW_STATE_CP_PENDING] = {
461bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
462bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_busy,
463d5afd5d1SCornelia Huck 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_request,
464bbe37e4cSDong Jia Shi 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
46562ec0d49SEric Farman 		[VFIO_CCW_EVENT_OPEN]		= fsm_notoper,
466f4b4ed44SEric Farman 		[VFIO_CCW_EVENT_CLOSE]		= fsm_close,
467bbe37e4cSDong Jia Shi 	},
468bbe37e4cSDong Jia Shi };
469