xref: /linux/drivers/s390/cio/vfio_ccw_fsm.c (revision 44f57d78)
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 	/* Issue "Start Subchannel" */
41 	ccode = ssch(sch->schid, orb);
42 
43 	switch (ccode) {
44 	case 0:
45 		/*
46 		 * Initialize device status information
47 		 */
48 		sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
49 		ret = 0;
50 		private->state = VFIO_CCW_STATE_CP_PENDING;
51 		break;
52 	case 1:		/* Status pending */
53 	case 2:		/* Busy */
54 		ret = -EBUSY;
55 		break;
56 	case 3:		/* Device/path not operational */
57 	{
58 		lpm = orb->cmd.lpm;
59 		if (lpm != 0)
60 			sch->lpm &= ~lpm;
61 		else
62 			sch->lpm = 0;
63 
64 		if (cio_update_schib(sch))
65 			ret = -ENODEV;
66 		else
67 			ret = sch->lpm ? -EACCES : -ENODEV;
68 		break;
69 	}
70 	default:
71 		ret = ccode;
72 	}
73 out:
74 	spin_unlock_irqrestore(sch->lock, flags);
75 	return ret;
76 }
77 
78 static int fsm_do_halt(struct vfio_ccw_private *private)
79 {
80 	struct subchannel *sch;
81 	unsigned long flags;
82 	int ccode;
83 	int ret;
84 
85 	sch = private->sch;
86 
87 	spin_lock_irqsave(sch->lock, flags);
88 
89 	/* Issue "Halt Subchannel" */
90 	ccode = hsch(sch->schid);
91 
92 	switch (ccode) {
93 	case 0:
94 		/*
95 		 * Initialize device status information
96 		 */
97 		sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
98 		ret = 0;
99 		break;
100 	case 1:		/* Status pending */
101 	case 2:		/* Busy */
102 		ret = -EBUSY;
103 		break;
104 	case 3:		/* Device not operational */
105 		ret = -ENODEV;
106 		break;
107 	default:
108 		ret = ccode;
109 	}
110 	spin_unlock_irqrestore(sch->lock, flags);
111 	return ret;
112 }
113 
114 static int fsm_do_clear(struct vfio_ccw_private *private)
115 {
116 	struct subchannel *sch;
117 	unsigned long flags;
118 	int ccode;
119 	int ret;
120 
121 	sch = private->sch;
122 
123 	spin_lock_irqsave(sch->lock, flags);
124 
125 	/* Issue "Clear Subchannel" */
126 	ccode = csch(sch->schid);
127 
128 	switch (ccode) {
129 	case 0:
130 		/*
131 		 * Initialize device status information
132 		 */
133 		sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND;
134 		/* TODO: check what else we might need to clear */
135 		ret = 0;
136 		break;
137 	case 3:		/* Device not operational */
138 		ret = -ENODEV;
139 		break;
140 	default:
141 		ret = ccode;
142 	}
143 	spin_unlock_irqrestore(sch->lock, flags);
144 	return ret;
145 }
146 
147 static void fsm_notoper(struct vfio_ccw_private *private,
148 			enum vfio_ccw_event event)
149 {
150 	struct subchannel *sch = private->sch;
151 
152 	/*
153 	 * TODO:
154 	 * Probably we should send the machine check to the guest.
155 	 */
156 	css_sched_sch_todo(sch, SCH_TODO_UNREG);
157 	private->state = VFIO_CCW_STATE_NOT_OPER;
158 }
159 
160 /*
161  * No operation action.
162  */
163 static void fsm_nop(struct vfio_ccw_private *private,
164 		    enum vfio_ccw_event event)
165 {
166 }
167 
168 static void fsm_io_error(struct vfio_ccw_private *private,
169 			 enum vfio_ccw_event event)
170 {
171 	pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state);
172 	private->io_region->ret_code = -EIO;
173 }
174 
175 static void fsm_io_busy(struct vfio_ccw_private *private,
176 			enum vfio_ccw_event event)
177 {
178 	private->io_region->ret_code = -EBUSY;
179 }
180 
181 static void fsm_io_retry(struct vfio_ccw_private *private,
182 			 enum vfio_ccw_event event)
183 {
184 	private->io_region->ret_code = -EAGAIN;
185 }
186 
187 static void fsm_async_error(struct vfio_ccw_private *private,
188 			    enum vfio_ccw_event event)
189 {
190 	struct ccw_cmd_region *cmd_region = private->cmd_region;
191 
192 	pr_err("vfio-ccw: FSM: %s request from state:%d\n",
193 	       cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" :
194 	       cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" :
195 	       "<unknown>", private->state);
196 	cmd_region->ret_code = -EIO;
197 }
198 
199 static void fsm_async_retry(struct vfio_ccw_private *private,
200 			    enum vfio_ccw_event event)
201 {
202 	private->cmd_region->ret_code = -EAGAIN;
203 }
204 
205 static void fsm_disabled_irq(struct vfio_ccw_private *private,
206 			     enum vfio_ccw_event event)
207 {
208 	struct subchannel *sch = private->sch;
209 
210 	/*
211 	 * An interrupt in a disabled state means a previous disable was not
212 	 * successful - should not happen, but we try to disable again.
213 	 */
214 	cio_disable_subchannel(sch);
215 }
216 inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
217 {
218 	return p->sch->schid;
219 }
220 
221 /*
222  * Deal with the ccw command request from the userspace.
223  */
224 static void fsm_io_request(struct vfio_ccw_private *private,
225 			   enum vfio_ccw_event event)
226 {
227 	union orb *orb;
228 	union scsw *scsw = &private->scsw;
229 	struct ccw_io_region *io_region = private->io_region;
230 	struct mdev_device *mdev = private->mdev;
231 	char *errstr = "request";
232 
233 	private->state = VFIO_CCW_STATE_CP_PROCESSING;
234 	memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
235 
236 	if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
237 		orb = (union orb *)io_region->orb_area;
238 
239 		/* Don't try to build a cp if transport mode is specified. */
240 		if (orb->tm.b) {
241 			io_region->ret_code = -EOPNOTSUPP;
242 			errstr = "transport mode";
243 			goto err_out;
244 		}
245 		io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
246 					      orb);
247 		if (io_region->ret_code) {
248 			errstr = "cp init";
249 			goto err_out;
250 		}
251 
252 		io_region->ret_code = cp_prefetch(&private->cp);
253 		if (io_region->ret_code) {
254 			errstr = "cp prefetch";
255 			cp_free(&private->cp);
256 			goto err_out;
257 		}
258 
259 		/* Start channel program and wait for I/O interrupt. */
260 		io_region->ret_code = fsm_io_helper(private);
261 		if (io_region->ret_code) {
262 			errstr = "cp fsm_io_helper";
263 			cp_free(&private->cp);
264 			goto err_out;
265 		}
266 		return;
267 	} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
268 		/* halt is handled via the async cmd region */
269 		io_region->ret_code = -EOPNOTSUPP;
270 		goto err_out;
271 	} else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
272 		/* clear is handled via the async cmd region */
273 		io_region->ret_code = -EOPNOTSUPP;
274 		goto err_out;
275 	}
276 
277 err_out:
278 	trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private),
279 			       io_region->ret_code, errstr);
280 }
281 
282 /*
283  * Deal with an async request from userspace.
284  */
285 static void fsm_async_request(struct vfio_ccw_private *private,
286 			      enum vfio_ccw_event event)
287 {
288 	struct ccw_cmd_region *cmd_region = private->cmd_region;
289 
290 	switch (cmd_region->command) {
291 	case VFIO_CCW_ASYNC_CMD_HSCH:
292 		cmd_region->ret_code = fsm_do_halt(private);
293 		break;
294 	case VFIO_CCW_ASYNC_CMD_CSCH:
295 		cmd_region->ret_code = fsm_do_clear(private);
296 		break;
297 	default:
298 		/* should not happen? */
299 		cmd_region->ret_code = -EINVAL;
300 	}
301 }
302 
303 /*
304  * Got an interrupt for a normal io (state busy).
305  */
306 static void fsm_irq(struct vfio_ccw_private *private,
307 		    enum vfio_ccw_event event)
308 {
309 	struct irb *irb = this_cpu_ptr(&cio_irb);
310 
311 	memcpy(&private->irb, irb, sizeof(*irb));
312 
313 	queue_work(vfio_ccw_work_q, &private->io_work);
314 
315 	if (private->completion)
316 		complete(private->completion);
317 }
318 
319 /*
320  * Device statemachine
321  */
322 fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = {
323 	[VFIO_CCW_STATE_NOT_OPER] = {
324 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_nop,
325 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_error,
326 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_error,
327 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_disabled_irq,
328 	},
329 	[VFIO_CCW_STATE_STANDBY] = {
330 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
331 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_error,
332 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_error,
333 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
334 	},
335 	[VFIO_CCW_STATE_IDLE] = {
336 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
337 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_request,
338 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_request,
339 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
340 	},
341 	[VFIO_CCW_STATE_CP_PROCESSING] = {
342 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
343 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_retry,
344 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_retry,
345 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
346 	},
347 	[VFIO_CCW_STATE_CP_PENDING] = {
348 		[VFIO_CCW_EVENT_NOT_OPER]	= fsm_notoper,
349 		[VFIO_CCW_EVENT_IO_REQ]		= fsm_io_busy,
350 		[VFIO_CCW_EVENT_ASYNC_REQ]	= fsm_async_request,
351 		[VFIO_CCW_EVENT_INTERRUPT]	= fsm_irq,
352 	},
353 };
354