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

vfio-ccw: add capabilities chain

Allow to extend the regions used by vfio-ccw. The first user will be
handling of halt and clear subchannel.

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

+201 -26
+161 -26
drivers/s390/cio/vfio_ccw_ops.c
··· 3 3 * Physical device callbacks for vfio_ccw 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/vfio.h> 12 14 #include <linux/mdev.h> 15 + #include <linux/nospec.h> 16 + #include <linux/slab.h> 13 17 14 18 #include "vfio_ccw_private.h" 15 19 ··· 161 157 { 162 158 struct vfio_ccw_private *private = 163 159 dev_get_drvdata(mdev_parent_dev(mdev)); 160 + int i; 164 161 165 162 vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, 166 163 &private->nb); 164 + 165 + for (i = 0; i < private->num_regions; i++) 166 + private->region[i].ops->release(private, &private->region[i]); 167 + 168 + private->num_regions = 0; 169 + kfree(private->region); 170 + private->region = NULL; 167 171 } 168 172 169 - static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev, 170 - char __user *buf, 171 - size_t count, 172 - loff_t *ppos) 173 + static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private, 174 + char __user *buf, size_t count, 175 + loff_t *ppos) 173 176 { 174 - struct vfio_ccw_private *private; 177 + loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 175 178 struct ccw_io_region *region; 176 179 int ret; 177 180 178 - if (*ppos + count > sizeof(*region)) 181 + if (pos + count > sizeof(*region)) 179 182 return -EINVAL; 180 183 181 - private = dev_get_drvdata(mdev_parent_dev(mdev)); 182 184 mutex_lock(&private->io_mutex); 183 185 region = private->io_region; 184 - if (copy_to_user(buf, (void *)region + *ppos, count)) 186 + if (copy_to_user(buf, (void *)region + pos, count)) 185 187 ret = -EFAULT; 186 188 else 187 189 ret = count; ··· 195 185 return ret; 196 186 } 197 187 198 - static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev, 199 - const char __user *buf, 200 - size_t count, 201 - loff_t *ppos) 188 + static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev, 189 + char __user *buf, 190 + size_t count, 191 + loff_t *ppos) 202 192 { 193 + unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos); 203 194 struct vfio_ccw_private *private; 195 + 196 + private = dev_get_drvdata(mdev_parent_dev(mdev)); 197 + 198 + if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions) 199 + return -EINVAL; 200 + 201 + switch (index) { 202 + case VFIO_CCW_CONFIG_REGION_INDEX: 203 + return vfio_ccw_mdev_read_io_region(private, buf, count, ppos); 204 + default: 205 + index -= VFIO_CCW_NUM_REGIONS; 206 + return private->region[index].ops->read(private, buf, count, 207 + ppos); 208 + } 209 + 210 + return -EINVAL; 211 + } 212 + 213 + static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private, 214 + const char __user *buf, 215 + size_t count, loff_t *ppos) 216 + { 217 + loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; 204 218 struct ccw_io_region *region; 205 219 int ret; 206 220 207 - if (*ppos + count > sizeof(*region)) 221 + if (pos + count > sizeof(*region)) 208 222 return -EINVAL; 209 223 210 - private = dev_get_drvdata(mdev_parent_dev(mdev)); 211 224 if (!mutex_trylock(&private->io_mutex)) 212 225 return -EAGAIN; 213 226 214 227 region = private->io_region; 215 - if (copy_from_user((void *)region + *ppos, buf, count)) { 228 + if (copy_from_user((void *)region + pos, buf, count)) { 216 229 ret = -EFAULT; 217 230 goto out_unlock; 218 231 } ··· 250 217 return ret; 251 218 } 252 219 253 - static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info) 220 + static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev, 221 + const char __user *buf, 222 + size_t count, 223 + loff_t *ppos) 254 224 { 225 + unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos); 226 + struct vfio_ccw_private *private; 227 + 228 + private = dev_get_drvdata(mdev_parent_dev(mdev)); 229 + 230 + if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions) 231 + return -EINVAL; 232 + 233 + switch (index) { 234 + case VFIO_CCW_CONFIG_REGION_INDEX: 235 + return vfio_ccw_mdev_write_io_region(private, buf, count, ppos); 236 + default: 237 + index -= VFIO_CCW_NUM_REGIONS; 238 + return private->region[index].ops->write(private, buf, count, 239 + ppos); 240 + } 241 + 242 + return -EINVAL; 243 + } 244 + 245 + static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info, 246 + struct mdev_device *mdev) 247 + { 248 + struct vfio_ccw_private *private; 249 + 250 + private = dev_get_drvdata(mdev_parent_dev(mdev)); 255 251 info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET; 256 - info->num_regions = VFIO_CCW_NUM_REGIONS; 252 + info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions; 257 253 info->num_irqs = VFIO_CCW_NUM_IRQS; 258 254 259 255 return 0; 260 256 } 261 257 262 258 static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info, 263 - u16 *cap_type_id, 264 - void **cap_type) 259 + struct mdev_device *mdev, 260 + unsigned long arg) 265 261 { 262 + struct vfio_ccw_private *private; 263 + int i; 264 + 265 + private = dev_get_drvdata(mdev_parent_dev(mdev)); 266 266 switch (info->index) { 267 267 case VFIO_CCW_CONFIG_REGION_INDEX: 268 268 info->offset = 0; ··· 303 237 info->flags = VFIO_REGION_INFO_FLAG_READ 304 238 | VFIO_REGION_INFO_FLAG_WRITE; 305 239 return 0; 306 - default: 307 - return -EINVAL; 240 + default: /* all other regions are handled via capability chain */ 241 + { 242 + struct vfio_info_cap caps = { .buf = NULL, .size = 0 }; 243 + struct vfio_region_info_cap_type cap_type = { 244 + .header.id = VFIO_REGION_INFO_CAP_TYPE, 245 + .header.version = 1 }; 246 + int ret; 247 + 248 + if (info->index >= 249 + VFIO_CCW_NUM_REGIONS + private->num_regions) 250 + return -EINVAL; 251 + 252 + info->index = array_index_nospec(info->index, 253 + VFIO_CCW_NUM_REGIONS + 254 + private->num_regions); 255 + 256 + i = info->index - VFIO_CCW_NUM_REGIONS; 257 + 258 + info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index); 259 + info->size = private->region[i].size; 260 + info->flags = private->region[i].flags; 261 + 262 + cap_type.type = private->region[i].type; 263 + cap_type.subtype = private->region[i].subtype; 264 + 265 + ret = vfio_info_add_capability(&caps, &cap_type.header, 266 + sizeof(cap_type)); 267 + if (ret) 268 + return ret; 269 + 270 + info->flags |= VFIO_REGION_INFO_FLAG_CAPS; 271 + if (info->argsz < sizeof(*info) + caps.size) { 272 + info->argsz = sizeof(*info) + caps.size; 273 + info->cap_offset = 0; 274 + } else { 275 + vfio_info_cap_shift(&caps, sizeof(*info)); 276 + if (copy_to_user((void __user *)arg + sizeof(*info), 277 + caps.buf, caps.size)) { 278 + kfree(caps.buf); 279 + return -EFAULT; 280 + } 281 + info->cap_offset = sizeof(*info); 282 + } 283 + 284 + kfree(caps.buf); 285 + 308 286 } 287 + } 288 + return 0; 309 289 } 310 290 311 291 static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info) ··· 428 316 } 429 317 } 430 318 319 + int vfio_ccw_register_dev_region(struct vfio_ccw_private *private, 320 + unsigned int subtype, 321 + const struct vfio_ccw_regops *ops, 322 + size_t size, u32 flags, void *data) 323 + { 324 + struct vfio_ccw_region *region; 325 + 326 + region = krealloc(private->region, 327 + (private->num_regions + 1) * sizeof(*region), 328 + GFP_KERNEL); 329 + if (!region) 330 + return -ENOMEM; 331 + 332 + private->region = region; 333 + private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW; 334 + private->region[private->num_regions].subtype = subtype; 335 + private->region[private->num_regions].ops = ops; 336 + private->region[private->num_regions].size = size; 337 + private->region[private->num_regions].flags = flags; 338 + private->region[private->num_regions].data = data; 339 + 340 + private->num_regions++; 341 + 342 + return 0; 343 + } 344 + 431 345 static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev, 432 346 unsigned int cmd, 433 347 unsigned long arg) ··· 474 336 if (info.argsz < minsz) 475 337 return -EINVAL; 476 338 477 - ret = vfio_ccw_mdev_get_device_info(&info); 339 + ret = vfio_ccw_mdev_get_device_info(&info, mdev); 478 340 if (ret) 479 341 return ret; 480 342 ··· 483 345 case VFIO_DEVICE_GET_REGION_INFO: 484 346 { 485 347 struct vfio_region_info info; 486 - u16 cap_type_id = 0; 487 - void *cap_type = NULL; 488 348 489 349 minsz = offsetofend(struct vfio_region_info, offset); 490 350 ··· 492 356 if (info.argsz < minsz) 493 357 return -EINVAL; 494 358 495 - ret = vfio_ccw_mdev_get_region_info(&info, &cap_type_id, 496 - &cap_type); 359 + ret = vfio_ccw_mdev_get_region_info(&info, mdev, arg); 497 360 if (ret) 498 361 return ret; 499 362
+38
drivers/s390/cio/vfio_ccw_private.h
··· 3 3 * Private stuff for vfio_ccw 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 #ifndef _VFIO_CCW_PRIVATE_H_ ··· 21 19 #include "css.h" 22 20 #include "vfio_ccw_cp.h" 23 21 22 + #define VFIO_CCW_OFFSET_SHIFT 10 23 + #define VFIO_CCW_OFFSET_TO_INDEX(off) (off >> VFIO_CCW_OFFSET_SHIFT) 24 + #define VFIO_CCW_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_CCW_OFFSET_SHIFT) 25 + #define VFIO_CCW_OFFSET_MASK (((u64)(1) << VFIO_CCW_OFFSET_SHIFT) - 1) 26 + 27 + /* capability chain handling similar to vfio-pci */ 28 + struct vfio_ccw_private; 29 + struct vfio_ccw_region; 30 + 31 + struct vfio_ccw_regops { 32 + ssize_t (*read)(struct vfio_ccw_private *private, char __user *buf, 33 + size_t count, loff_t *ppos); 34 + ssize_t (*write)(struct vfio_ccw_private *private, 35 + const char __user *buf, size_t count, loff_t *ppos); 36 + void (*release)(struct vfio_ccw_private *private, 37 + struct vfio_ccw_region *region); 38 + }; 39 + 40 + struct vfio_ccw_region { 41 + u32 type; 42 + u32 subtype; 43 + const struct vfio_ccw_regops *ops; 44 + void *data; 45 + size_t size; 46 + u32 flags; 47 + }; 48 + 49 + int vfio_ccw_register_dev_region(struct vfio_ccw_private *private, 50 + unsigned int subtype, 51 + const struct vfio_ccw_regops *ops, 52 + size_t size, u32 flags, void *data); 53 + 24 54 /** 25 55 * struct vfio_ccw_private 26 56 * @sch: pointer to the subchannel ··· 63 29 * @nb: notifier for vfio events 64 30 * @io_region: MMIO region to input/output I/O arguments/results 65 31 * @io_mutex: protect against concurrent update of I/O regions 32 + * @region: additional regions for other subchannel operations 33 + * @num_regions: number of additional regions 66 34 * @cp: channel program for the current I/O operation 67 35 * @irb: irb info received from interrupt 68 36 * @scsw: scsw info ··· 80 44 struct notifier_block nb; 81 45 struct ccw_io_region *io_region; 82 46 struct mutex io_mutex; 47 + struct vfio_ccw_region *region; 48 + int num_regions; 83 49 84 50 struct channel_program cp; 85 51 struct irb irb;
+2
include/uapi/linux/vfio.h
··· 353 353 #define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2 354 354 }; 355 355 356 + #define VFIO_REGION_TYPE_CCW (2) 357 + 356 358 /* 357 359 * 10de vendor sub-type 358 360 *