Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vfio-ccw: add handling for async channel instructions

Add a region to the vfio-ccw device that can be used to submit
asynchronous I/O instructions. ssch continues to be handled by the
existing I/O region; the new region handles hsch and csch.

Interrupt status continues to be reported through the same channels
as for ssch.

Acked-by: Eric Farman <farman@linux.ibm.com>
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>

+270 -18
+2 -1
drivers/s390/cio/Makefile
··· 20 20 qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o 21 21 obj-$(CONFIG_QDIO) += qdio.o 22 22 23 - vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o 23 + vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o \ 24 + vfio_ccw_async.o 24 25 obj-$(CONFIG_VFIO_CCW) += vfio_ccw.o
+88
drivers/s390/cio/vfio_ccw_async.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Async I/O region for vfio_ccw 4 + * 5 + * Copyright Red Hat, Inc. 2019 6 + * 7 + * Author(s): Cornelia Huck <cohuck@redhat.com> 8 + */ 9 + 10 + #include <linux/vfio.h> 11 + #include <linux/mdev.h> 12 + 13 + #include "vfio_ccw_private.h" 14 + 15 + static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private, 16 + char __user *buf, size_t count, 17 + loff_t *ppos) 18 + { 19 + unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; 20 + loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 21 + struct ccw_cmd_region *region; 22 + int ret; 23 + 24 + if (pos + count > sizeof(*region)) 25 + return -EINVAL; 26 + 27 + mutex_lock(&private->io_mutex); 28 + region = private->region[i].data; 29 + if (copy_to_user(buf, (void *)region + pos, count)) 30 + ret = -EFAULT; 31 + else 32 + ret = count; 33 + mutex_unlock(&private->io_mutex); 34 + return ret; 35 + } 36 + 37 + static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private, 38 + const char __user *buf, size_t count, 39 + loff_t *ppos) 40 + { 41 + unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS; 42 + loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 43 + struct ccw_cmd_region *region; 44 + int ret; 45 + 46 + if (pos + count > sizeof(*region)) 47 + return -EINVAL; 48 + 49 + if (!mutex_trylock(&private->io_mutex)) 50 + return -EAGAIN; 51 + 52 + region = private->region[i].data; 53 + if (copy_from_user((void *)region + pos, buf, count)) { 54 + ret = -EFAULT; 55 + goto out_unlock; 56 + } 57 + 58 + vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_ASYNC_REQ); 59 + 60 + ret = region->ret_code ? region->ret_code : count; 61 + 62 + out_unlock: 63 + mutex_unlock(&private->io_mutex); 64 + return ret; 65 + } 66 + 67 + static void vfio_ccw_async_region_release(struct vfio_ccw_private *private, 68 + struct vfio_ccw_region *region) 69 + { 70 + 71 + } 72 + 73 + const struct vfio_ccw_regops vfio_ccw_async_region_ops = { 74 + .read = vfio_ccw_async_region_read, 75 + .write = vfio_ccw_async_region_write, 76 + .release = vfio_ccw_async_region_release, 77 + }; 78 + 79 + int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private) 80 + { 81 + return vfio_ccw_register_dev_region(private, 82 + VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, 83 + &vfio_ccw_async_region_ops, 84 + sizeof(struct ccw_cmd_region), 85 + VFIO_REGION_INFO_FLAG_READ | 86 + VFIO_REGION_INFO_FLAG_WRITE, 87 + private->cmd_region); 88 + }
+33 -13
drivers/s390/cio/vfio_ccw_drv.c
··· 3 3 * VFIO based Physical Subchannel device driver 4 4 * 5 5 * Copyright IBM Corp. 2017 6 + * Copyright Red Hat, Inc. 2019 6 7 * 7 8 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 8 9 * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> 10 + * Cornelia Huck <cohuck@redhat.com> 9 11 */ 10 12 11 13 #include <linux/module.h> ··· 25 23 26 24 struct workqueue_struct *vfio_ccw_work_q; 27 25 static struct kmem_cache *vfio_ccw_io_region; 26 + static struct kmem_cache *vfio_ccw_cmd_region; 28 27 29 28 /* 30 29 * Helpers ··· 113 110 { 114 111 struct pmcw *pmcw = &sch->schib.pmcw; 115 112 struct vfio_ccw_private *private; 116 - int ret; 113 + int ret = -ENOMEM; 117 114 118 115 if (pmcw->qf) { 119 116 dev_warn(&sch->dev, "vfio: ccw: does not support QDIO: %s\n", ··· 127 124 128 125 private->io_region = kmem_cache_zalloc(vfio_ccw_io_region, 129 126 GFP_KERNEL | GFP_DMA); 130 - if (!private->io_region) { 131 - kfree(private); 132 - return -ENOMEM; 133 - } 127 + if (!private->io_region) 128 + goto out_free; 129 + 130 + private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region, 131 + GFP_KERNEL | GFP_DMA); 132 + if (!private->cmd_region) 133 + goto out_free; 134 134 135 135 private->sch = sch; 136 136 dev_set_drvdata(&sch->dev, private); ··· 161 155 cio_disable_subchannel(sch); 162 156 out_free: 163 157 dev_set_drvdata(&sch->dev, NULL); 164 - kmem_cache_free(vfio_ccw_io_region, private->io_region); 158 + if (private->cmd_region) 159 + kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region); 160 + if (private->io_region) 161 + kmem_cache_free(vfio_ccw_io_region, private->io_region); 165 162 kfree(private); 166 163 return ret; 167 164 } ··· 179 170 180 171 dev_set_drvdata(&sch->dev, NULL); 181 172 173 + kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region); 182 174 kmem_cache_free(vfio_ccw_io_region, private->io_region); 183 175 kfree(private); 184 176 ··· 254 244 255 245 static int __init vfio_ccw_sch_init(void) 256 246 { 257 - int ret; 247 + int ret = -ENOMEM; 258 248 259 249 vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw"); 260 250 if (!vfio_ccw_work_q) ··· 264 254 sizeof(struct ccw_io_region), 0, 265 255 SLAB_ACCOUNT, 0, 266 256 sizeof(struct ccw_io_region), NULL); 267 - if (!vfio_ccw_io_region) { 268 - destroy_workqueue(vfio_ccw_work_q); 269 - return -ENOMEM; 270 - } 257 + if (!vfio_ccw_io_region) 258 + goto out_err; 259 + 260 + vfio_ccw_cmd_region = kmem_cache_create_usercopy("vfio_ccw_cmd_region", 261 + sizeof(struct ccw_cmd_region), 0, 262 + SLAB_ACCOUNT, 0, 263 + sizeof(struct ccw_cmd_region), NULL); 264 + if (!vfio_ccw_cmd_region) 265 + goto out_err; 271 266 272 267 isc_register(VFIO_CCW_ISC); 273 268 ret = css_driver_register(&vfio_ccw_sch_driver); 274 269 if (ret) { 275 270 isc_unregister(VFIO_CCW_ISC); 276 - kmem_cache_destroy(vfio_ccw_io_region); 277 - destroy_workqueue(vfio_ccw_work_q); 271 + goto out_err; 278 272 } 279 273 274 + return ret; 275 + 276 + out_err: 277 + kmem_cache_destroy(vfio_ccw_cmd_region); 278 + kmem_cache_destroy(vfio_ccw_io_region); 279 + destroy_workqueue(vfio_ccw_work_q); 280 280 return ret; 281 281 } 282 282
+117 -2
drivers/s390/cio/vfio_ccw_fsm.c
··· 3 3 * Finite state machine for vfio-ccw device handling 4 4 * 5 5 * Copyright IBM Corp. 2017 6 + * Copyright Red Hat, Inc. 2019 6 7 * 7 8 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 9 + * Cornelia Huck <cohuck@redhat.com> 8 10 */ 9 11 10 12 #include <linux/vfio.h> ··· 75 73 return ret; 76 74 } 77 75 76 + static int fsm_do_halt(struct vfio_ccw_private *private) 77 + { 78 + struct subchannel *sch; 79 + unsigned long flags; 80 + int ccode; 81 + int ret; 82 + 83 + sch = private->sch; 84 + 85 + spin_lock_irqsave(sch->lock, flags); 86 + 87 + /* Issue "Halt Subchannel" */ 88 + ccode = hsch(sch->schid); 89 + 90 + switch (ccode) { 91 + case 0: 92 + /* 93 + * Initialize device status information 94 + */ 95 + sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; 96 + ret = 0; 97 + break; 98 + case 1: /* Status pending */ 99 + case 2: /* Busy */ 100 + ret = -EBUSY; 101 + break; 102 + case 3: /* Device not operational */ 103 + ret = -ENODEV; 104 + break; 105 + default: 106 + ret = ccode; 107 + } 108 + spin_unlock_irqrestore(sch->lock, flags); 109 + return ret; 110 + } 111 + 112 + static int fsm_do_clear(struct vfio_ccw_private *private) 113 + { 114 + struct subchannel *sch; 115 + unsigned long flags; 116 + int ccode; 117 + int ret; 118 + 119 + sch = private->sch; 120 + 121 + spin_lock_irqsave(sch->lock, flags); 122 + 123 + /* Issue "Clear Subchannel" */ 124 + ccode = csch(sch->schid); 125 + 126 + switch (ccode) { 127 + case 0: 128 + /* 129 + * Initialize device status information 130 + */ 131 + sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND; 132 + /* TODO: check what else we might need to clear */ 133 + ret = 0; 134 + break; 135 + case 3: /* Device not operational */ 136 + ret = -ENODEV; 137 + break; 138 + default: 139 + ret = ccode; 140 + } 141 + spin_unlock_irqrestore(sch->lock, flags); 142 + return ret; 143 + } 144 + 78 145 static void fsm_notoper(struct vfio_ccw_private *private, 79 146 enum vfio_ccw_event event) 80 147 { ··· 182 111 enum vfio_ccw_event event) 183 112 { 184 113 private->io_region->ret_code = -EAGAIN; 114 + } 115 + 116 + static void fsm_async_error(struct vfio_ccw_private *private, 117 + enum vfio_ccw_event event) 118 + { 119 + struct ccw_cmd_region *cmd_region = private->cmd_region; 120 + 121 + pr_err("vfio-ccw: FSM: %s request from state:%d\n", 122 + cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" : 123 + cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" : 124 + "<unknown>", private->state); 125 + cmd_region->ret_code = -EIO; 126 + } 127 + 128 + static void fsm_async_retry(struct vfio_ccw_private *private, 129 + enum vfio_ccw_event event) 130 + { 131 + private->cmd_region->ret_code = -EAGAIN; 185 132 } 186 133 187 134 static void fsm_disabled_irq(struct vfio_ccw_private *private, ··· 265 176 } 266 177 return; 267 178 } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) { 268 - /* XXX: Handle halt. */ 179 + /* halt is handled via the async cmd region */ 269 180 io_region->ret_code = -EOPNOTSUPP; 270 181 goto err_out; 271 182 } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { 272 - /* XXX: Handle clear. */ 183 + /* clear is handled via the async cmd region */ 273 184 io_region->ret_code = -EOPNOTSUPP; 274 185 goto err_out; 275 186 } ··· 277 188 err_out: 278 189 trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private), 279 190 io_region->ret_code, errstr); 191 + } 192 + 193 + /* 194 + * Deal with an async request from userspace. 195 + */ 196 + static void fsm_async_request(struct vfio_ccw_private *private, 197 + enum vfio_ccw_event event) 198 + { 199 + struct ccw_cmd_region *cmd_region = private->cmd_region; 200 + 201 + switch (cmd_region->command) { 202 + case VFIO_CCW_ASYNC_CMD_HSCH: 203 + cmd_region->ret_code = fsm_do_halt(private); 204 + break; 205 + case VFIO_CCW_ASYNC_CMD_CSCH: 206 + cmd_region->ret_code = fsm_do_clear(private); 207 + break; 208 + default: 209 + /* should not happen? */ 210 + cmd_region->ret_code = -EINVAL; 211 + } 280 212 } 281 213 282 214 /* ··· 323 213 [VFIO_CCW_STATE_NOT_OPER] = { 324 214 [VFIO_CCW_EVENT_NOT_OPER] = fsm_nop, 325 215 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 216 + [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error, 326 217 [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq, 327 218 }, 328 219 [VFIO_CCW_STATE_STANDBY] = { 329 220 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 330 221 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 222 + [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error, 331 223 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 332 224 }, 333 225 [VFIO_CCW_STATE_IDLE] = { 334 226 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 335 227 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request, 228 + [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request, 336 229 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 337 230 }, 338 231 [VFIO_CCW_STATE_CP_PROCESSING] = { 339 232 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 340 233 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_retry, 234 + [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_retry, 341 235 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 342 236 }, 343 237 [VFIO_CCW_STATE_CP_PENDING] = { 344 238 [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 345 239 [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, 240 + [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request, 346 241 [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 347 242 }, 348 243 };
+11 -2
drivers/s390/cio/vfio_ccw_ops.c
··· 150 150 struct vfio_ccw_private *private = 151 151 dev_get_drvdata(mdev_parent_dev(mdev)); 152 152 unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP; 153 + int ret; 153 154 154 155 private->nb.notifier_call = vfio_ccw_mdev_notifier; 155 156 156 - return vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, 157 - &events, &private->nb); 157 + ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, 158 + &events, &private->nb); 159 + if (ret) 160 + return ret; 161 + 162 + ret = vfio_ccw_register_async_dev_regions(private); 163 + if (ret) 164 + vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, 165 + &private->nb); 166 + return ret; 158 167 } 159 168 160 169 static void vfio_ccw_mdev_release(struct mdev_device *mdev)
+5
drivers/s390/cio/vfio_ccw_private.h
··· 53 53 const struct vfio_ccw_regops *ops, 54 54 size_t size, u32 flags, void *data); 55 55 56 + int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private); 57 + 56 58 /** 57 59 * struct vfio_ccw_private 58 60 * @sch: pointer to the subchannel ··· 66 64 * @io_region: MMIO region to input/output I/O arguments/results 67 65 * @io_mutex: protect against concurrent update of I/O regions 68 66 * @region: additional regions for other subchannel operations 67 + * @cmd_region: MMIO region for asynchronous I/O commands other than START 69 68 * @num_regions: number of additional regions 70 69 * @cp: channel program for the current I/O operation 71 70 * @irb: irb info received from interrupt ··· 84 81 struct ccw_io_region *io_region; 85 82 struct mutex io_mutex; 86 83 struct vfio_ccw_region *region; 84 + struct ccw_cmd_region *cmd_region; 87 85 int num_regions; 88 86 89 87 struct channel_program cp; ··· 120 116 VFIO_CCW_EVENT_NOT_OPER, 121 117 VFIO_CCW_EVENT_IO_REQ, 122 118 VFIO_CCW_EVENT_INTERRUPT, 119 + VFIO_CCW_EVENT_ASYNC_REQ, 123 120 /* last element! */ 124 121 NR_VFIO_CCW_EVENTS 125 122 };
+2
include/uapi/linux/vfio.h
··· 354 354 }; 355 355 356 356 #define VFIO_REGION_TYPE_CCW (2) 357 + /* ccw sub-types */ 358 + #define VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD (1) 357 359 358 360 /* 359 361 * 10de vendor sub-type
+12
include/uapi/linux/vfio_ccw.h
··· 12 12 13 13 #include <linux/types.h> 14 14 15 + /* used for START SUBCHANNEL, always present */ 15 16 struct ccw_io_region { 16 17 #define ORB_AREA_SIZE 12 17 18 __u8 orb_area[ORB_AREA_SIZE]; ··· 21 20 #define IRB_AREA_SIZE 96 22 21 __u8 irb_area[IRB_AREA_SIZE]; 23 22 __u32 ret_code; 23 + } __packed; 24 + 25 + /* 26 + * used for processing commands that trigger asynchronous actions 27 + * Note: this is controlled by a capability 28 + */ 29 + #define VFIO_CCW_ASYNC_CMD_HSCH (1 << 0) 30 + #define VFIO_CCW_ASYNC_CMD_CSCH (1 << 1) 31 + struct ccw_cmd_region { 32 + __u32 command; 33 + __u32 ret_code; 24 34 } __packed; 25 35 26 36 #endif