| @@ -29,15 +29,18 @@ · 28 unmodified lines |
| 29 | 29 | #include <linux/seq_file.h> |
| 30 | 30 | #include <linux/miscdevice.h> |
| 31 | 31 | #include <linux/moduleparam.h> |
| 32 | +#include <linux/virtio_mmio.h> |
| 32 | 33 | |
| 33 | 34 | #include <asm/xen/hypervisor.h> |
| 34 | 35 | #include <asm/xen/hypercall.h> |
| 35 | 36 | |
| 36 | 37 | #include <xen/xen.h> |
| 38 | +#include <xen/events.h> |
| 37 | 39 | #include <xen/privcmd.h> |
| 38 | 40 | #include <xen/interface/xen.h> |
| 39 | 41 | #include <xen/interface/memory.h> |
| 40 | 42 | #include <xen/interface/hvm/dm_op.h> |
| 43 | +#include <xen/interface/hvm/ioreq.h> |
| 41 | 44 | #include <xen/features.h> |
| 42 | 45 | #include <xen/page.h> |
| 43 | 46 | #include <xen/xen-ops.h> |
| @@ -782,6 +785,7 @@ static long privcmd_ioctl_mmap_resource(struct file *file, · 738 unmodified lines |
| 782 | 785 | goto out; |
| 783 | 786 | |
| 784 | 787 | pages = vma->vm_private_data; |
| 788 | + |
| 785 | 789 | for (i = 0; i < kdata.num; i++) { |
| 786 | 790 | xen_pfn_t pfn = |
| 787 | 791 | page_to_xen_pfn(pages[i / XEN_PFN_PER_PAGE]); |
| @@ -838,7 +842,7 @@ out: · 50 unmodified lines |
| 838 | 842 | return rc; |
| 839 | 843 | } |
| 840 | 844 | |
| 841 | | −#ifdef CONFIG_XEN_PRIVCMD_IRQFD |
| 845 | +#ifdef CONFIG_XEN_PRIVCMD_EVENTFD |
| 842 | 846 | /* Irqfd support */ |
| 843 | 847 | static struct workqueue_struct *irqfd_cleanup_wq; |
| 844 | 848 | static DEFINE_MUTEX(irqfds_lock); |
| @@ -935,7 +939,7 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd) · 90 unmodified lines |
| 935 | 939 | return -ENOMEM; |
| 936 | 940 | dm_op = kirqfd + 1; |
| 937 | 941 | |
| 938 | | − if (copy_from_user(dm_op, irqfd->dm_op, irqfd->size)) { |
| 942 | + if (copy_from_user(dm_op, u64_to_user_ptr(irqfd->dm_op), irqfd->size)) { |
| 939 | 943 | ret = -EFAULT; |
| 940 | 944 | goto error_kfree; |
| 941 | 945 | } |
| @@ -1079,6 +1083,389 @@ static void privcmd_irqfd_exit(void) · 137 unmodified lines |
| 1079 | 1083 | |
| 1080 | 1084 | destroy_workqueue(irqfd_cleanup_wq); |
| 1081 | 1085 | } |
| 1086 | + |
| 1087 | +/* Ioeventfd Support */ |
| 1088 | +#define QUEUE_NOTIFY_VQ_MASK 0xFFFF |
| 1089 | + |
| 1090 | +static DEFINE_MUTEX(ioreq_lock); |
| 1091 | +static LIST_HEAD(ioreq_list); |
| 1092 | + |
| 1093 | +/* per-eventfd structure */ |
| 1094 | +struct privcmd_kernel_ioeventfd { |
| 1095 | + struct eventfd_ctx *eventfd; |
| 1096 | + struct list_head list; |
| 1097 | + u64 addr; |
| 1098 | + unsigned int addr_len; |
| 1099 | + unsigned int vq; |
| 1100 | +}; |
| 1101 | + |
| 1102 | +/* per-guest CPU / port structure */ |
| 1103 | +struct ioreq_port { |
| 1104 | + int vcpu; |
| 1105 | + unsigned int port; |
| 1106 | + struct privcmd_kernel_ioreq *kioreq; |
| 1107 | +}; |
| 1108 | + |
| 1109 | +/* per-guest structure */ |
| 1110 | +struct privcmd_kernel_ioreq { |
| 1111 | + domid_t dom; |
| 1112 | + unsigned int vcpus; |
| 1113 | + u64 uioreq; |
| 1114 | + struct ioreq *ioreq; |
| 1115 | + spinlock_t lock; /* Protects ioeventfds list */ |
| 1116 | + struct list_head ioeventfds; |
| 1117 | + struct list_head list; |
| 1118 | + struct ioreq_port ports[0]; |
| 1119 | +}; |
| 1120 | + |
| 1121 | +static irqreturn_t ioeventfd_interrupt(int irq, void *dev_id) |
| 1122 | +{ |
| 1123 | + struct ioreq_port *port = dev_id; |
| 1124 | + struct privcmd_kernel_ioreq *kioreq = port->kioreq; |
| 1125 | + struct ioreq *ioreq = &kioreq->ioreq[port->vcpu]; |
| 1126 | + struct privcmd_kernel_ioeventfd *kioeventfd; |
| 1127 | + unsigned int state = STATE_IOREQ_READY; |
| 1128 | + |
| 1129 | + if (ioreq->state != STATE_IOREQ_READY || |
| 1130 | + ioreq->type != IOREQ_TYPE_COPY || ioreq->dir != IOREQ_WRITE) |
| 1131 | + return IRQ_NONE; |
| 1132 | + |
| 1133 | + /* |
| 1134 | + * We need a barrier, smp_mb(), here to ensure reads are finished before |
| 1135 | + * `state` is updated. Since the lock implementation ensures that |
| 1136 | + * appropriate barrier will be added anyway, we can avoid adding |
| 1137 | + * explicit barrier here. |
| 1138 | + * |
| 1139 | + * Ideally we don't need to update `state` within the locks, but we do |
| 1140 | + * that here to avoid adding explicit barrier. |
| 1141 | + */ |
| 1142 | + |
| 1143 | + spin_lock(&kioreq->lock); |
| 1144 | + ioreq->state = STATE_IOREQ_INPROCESS; |
| 1145 | + |
| 1146 | + list_for_each_entry(kioeventfd, &kioreq->ioeventfds, list) { |
| 1147 | + if (ioreq->addr == kioeventfd->addr + VIRTIO_MMIO_QUEUE_NOTIFY && |
| 1148 | + ioreq->size == kioeventfd->addr_len && |
| 1149 | + (ioreq->data & QUEUE_NOTIFY_VQ_MASK) == kioeventfd->vq) { |
| 1150 | + eventfd_signal(kioeventfd->eventfd, 1); |
| 1151 | + state = STATE_IORESP_READY; |
| 1152 | + break; |
| 1153 | + } |
| 1154 | + } |
| 1155 | + spin_unlock(&kioreq->lock); |
| 1156 | + |
| 1157 | + /* |
| 1158 | + * We need a barrier, smp_mb(), here to ensure writes are finished |
| 1159 | + * before `state` is updated. Since the lock implementation ensures that |
| 1160 | + * appropriate barrier will be added anyway, we can avoid adding |
| 1161 | + * explicit barrier here. |
| 1162 | + */ |
| 1163 | + |
| 1164 | + ioreq->state = state; |
| 1165 | + |
| 1166 | + if (state == STATE_IORESP_READY) { |
| 1167 | + notify_remote_via_evtchn(port->port); |
| 1168 | + return IRQ_HANDLED; |
| 1169 | + } |
| 1170 | + |
| 1171 | + return IRQ_NONE; |
| 1172 | +} |
| 1173 | + |
| 1174 | +static void ioreq_free(struct privcmd_kernel_ioreq *kioreq) |
| 1175 | +{ |
| 1176 | + struct ioreq_port *ports = kioreq->ports; |
| 1177 | + int i; |
| 1178 | + |
| 1179 | + lockdep_assert_held(&ioreq_lock); |
| 1180 | + |
| 1181 | + list_del(&kioreq->list); |
| 1182 | + |
| 1183 | + for (i = kioreq->vcpus - 1; i >= 0; i--) |
| 1184 | + unbind_from_irqhandler(irq_from_evtchn(ports[i].port), &ports[i]); |
| 1185 | + |
| 1186 | + kfree(kioreq); |
| 1187 | +} |
| 1188 | + |
| 1189 | +static |
| 1190 | +struct privcmd_kernel_ioreq *alloc_ioreq(struct privcmd_ioeventfd *ioeventfd) |
| 1191 | +{ |
| 1192 | + struct privcmd_kernel_ioreq *kioreq; |
| 1193 | + struct mm_struct *mm = current->mm; |
| 1194 | + struct vm_area_struct *vma; |
| 1195 | + struct page **pages; |
| 1196 | + unsigned int *ports; |
| 1197 | + int ret, size, i; |
| 1198 | + |
| 1199 | + lockdep_assert_held(&ioreq_lock); |
| 1200 | + |
| 1201 | + size = struct_size(kioreq, ports, ioeventfd->vcpus); |
| 1202 | + kioreq = kzalloc(size, GFP_KERNEL); |
| 1203 | + if (!kioreq) |
| 1204 | + return ERR_PTR(-ENOMEM); |
| 1205 | + |
| 1206 | + kioreq->dom = ioeventfd->dom; |
| 1207 | + kioreq->vcpus = ioeventfd->vcpus; |
| 1208 | + kioreq->uioreq = ioeventfd->ioreq; |
| 1209 | + spin_lock_init(&kioreq->lock); |
| 1210 | + INIT_LIST_HEAD(&kioreq->ioeventfds); |
| 1211 | + |
| 1212 | + /* The memory for ioreq server must have been mapped earlier */ |
| 1213 | + mmap_write_lock(mm); |
| 1214 | + vma = find_vma(mm, (unsigned long)ioeventfd->ioreq); |
| 1215 | + if (!vma) { |
| 1216 | + pr_err("Failed to find vma for ioreq page!\n"); |
| 1217 | + mmap_write_unlock(mm); |
| 1218 | + ret = -EFAULT; |
| 1219 | + goto error_kfree; |
| 1220 | + } |
| 1221 | + |
| 1222 | + pages = vma->vm_private_data; |
| 1223 | + kioreq->ioreq = (struct ioreq *)(page_to_virt(pages[0])); |
| 1224 | + mmap_write_unlock(mm); |
| 1225 | + |
| 1226 | + size = sizeof(*ports) * kioreq->vcpus; |
| 1227 | + ports = kzalloc(size, GFP_KERNEL); |
| 1228 | + if (!ports) { |
| 1229 | + ret = -ENOMEM; |
| 1230 | + goto error_kfree; |
| 1231 | + } |
| 1232 | + |
| 1233 | + if (copy_from_user(ports, u64_to_user_ptr(ioeventfd->ports), size)) { |
| 1234 | + ret = -EFAULT; |
| 1235 | + goto error_kfree_ports; |
| 1236 | + } |
| 1237 | + |
| 1238 | + for (i = 0; i < kioreq->vcpus; i++) { |
| 1239 | + kioreq->ports[i].vcpu = i; |
| 1240 | + kioreq->ports[i].port = ports[i]; |
| 1241 | + kioreq->ports[i].kioreq = kioreq; |
| 1242 | + |
| 1243 | + ret = bind_evtchn_to_irqhandler_lateeoi(ports[i], |
| 1244 | + ioeventfd_interrupt, IRQF_SHARED, "ioeventfd", |
| 1245 | + &kioreq->ports[i]); |
| 1246 | + if (ret < 0) |
| 1247 | + goto error_unbind; |
| 1248 | + } |
| 1249 | + |
| 1250 | + kfree(ports); |
| 1251 | + |
| 1252 | + list_add_tail(&kioreq->list, &ioreq_list); |
| 1253 | + |
| 1254 | + return kioreq; |
| 1255 | + |
| 1256 | +error_unbind: |
| 1257 | + while (--i >= 0) |
| 1258 | + unbind_from_irqhandler(irq_from_evtchn(ports[i]), &kioreq->ports[i]); |
| 1259 | +error_kfree_ports: |
| 1260 | + kfree(ports); |
| 1261 | +error_kfree: |
| 1262 | + kfree(kioreq); |
| 1263 | + return ERR_PTR(ret); |
| 1264 | +} |
| 1265 | + |
| 1266 | +static struct privcmd_kernel_ioreq * |
| 1267 | +get_ioreq(struct privcmd_ioeventfd *ioeventfd, struct eventfd_ctx *eventfd) |
| 1268 | +{ |
| 1269 | + struct privcmd_kernel_ioreq *kioreq; |
| 1270 | + unsigned long flags; |
| 1271 | + |
| 1272 | + list_for_each_entry(kioreq, &ioreq_list, list) { |
| 1273 | + struct privcmd_kernel_ioeventfd *kioeventfd; |
| 1274 | + |
| 1275 | + /* |
| 1276 | + * kioreq fields can be accessed here without a lock as they are |
| 1277 | + * never updated after being added to the ioreq_list. |
| 1278 | + */ |
| 1279 | + if (kioreq->uioreq != ioeventfd->ioreq) { |
| 1280 | + continue; |
| 1281 | + } else if (kioreq->dom != ioeventfd->dom || |
| 1282 | + kioreq->vcpus != ioeventfd->vcpus) { |
| 1283 | + pr_err("Invalid ioeventfd configuration mismatch, dom (%u vs %u), vcpus (%u vs %u)\n", |
| 1284 | + kioreq->dom, ioeventfd->dom, kioreq->vcpus, |
| 1285 | + ioeventfd->vcpus); |
| 1286 | + return ERR_PTR(-EINVAL); |
| 1287 | + } |
| 1288 | + |
| 1289 | + /* Look for a duplicate eventfd for the same guest */ |
| 1290 | + spin_lock_irqsave(&kioreq->lock, flags); |
| 1291 | + list_for_each_entry(kioeventfd, &kioreq->ioeventfds, list) { |
| 1292 | + if (eventfd == kioeventfd->eventfd) { |
| 1293 | + spin_unlock_irqrestore(&kioreq->lock, flags); |
| 1294 | + return ERR_PTR(-EBUSY); |
| 1295 | + } |
| 1296 | + } |
| 1297 | + spin_unlock_irqrestore(&kioreq->lock, flags); |
| 1298 | + |
| 1299 | + return kioreq; |
| 1300 | + } |
| 1301 | + |
| 1302 | + /* Matching kioreq isn't found, allocate a new one */ |
| 1303 | + return alloc_ioreq(ioeventfd); |
| 1304 | +} |
| 1305 | + |
| 1306 | +static void ioeventfd_free(struct privcmd_kernel_ioeventfd *kioeventfd) |
| 1307 | +{ |
| 1308 | + list_del(&kioeventfd->list); |
| 1309 | + eventfd_ctx_put(kioeventfd->eventfd); |
| 1310 | + kfree(kioeventfd); |
| 1311 | +} |
| 1312 | + |
| 1313 | +static int privcmd_ioeventfd_assign(struct privcmd_ioeventfd *ioeventfd) |
| 1314 | +{ |
| 1315 | + struct privcmd_kernel_ioeventfd *kioeventfd; |
| 1316 | + struct privcmd_kernel_ioreq *kioreq; |
| 1317 | + unsigned long flags; |
| 1318 | + struct fd f; |
| 1319 | + int ret; |
| 1320 | + |
| 1321 | + /* Check for range overflow */ |
| 1322 | + if (ioeventfd->addr + ioeventfd->addr_len < ioeventfd->addr) |
| 1323 | + return -EINVAL; |
| 1324 | + |
| 1325 | + /* Vhost requires us to support length 1, 2, 4, and 8 */ |
| 1326 | + if (!(ioeventfd->addr_len == 1 || ioeventfd->addr_len == 2 || |
| 1327 | + ioeventfd->addr_len == 4 || ioeventfd->addr_len == 8)) |
| 1328 | + return -EINVAL; |
| 1329 | + |
| 1330 | + /* 4096 vcpus limit enough ? */ |
| 1331 | + if (!ioeventfd->vcpus || ioeventfd->vcpus > 4096) |
| 1332 | + return -EINVAL; |
| 1333 | + |
| 1334 | + kioeventfd = kzalloc(sizeof(*kioeventfd), GFP_KERNEL); |
| 1335 | + if (!kioeventfd) |
| 1336 | + return -ENOMEM; |
| 1337 | + |
| 1338 | + f = fdget(ioeventfd->event_fd); |
| 1339 | + if (!f.file) { |
| 1340 | + ret = -EBADF; |
| 1341 | + goto error_kfree; |
| 1342 | + } |
| 1343 | + |
| 1344 | + kioeventfd->eventfd = eventfd_ctx_fileget(f.file); |
| 1345 | + fdput(f); |
| 1346 | + |
| 1347 | + if (IS_ERR(kioeventfd->eventfd)) { |
| 1348 | + ret = PTR_ERR(kioeventfd->eventfd); |
| 1349 | + goto error_kfree; |
| 1350 | + } |
| 1351 | + |
| 1352 | + kioeventfd->addr = ioeventfd->addr; |
| 1353 | + kioeventfd->addr_len = ioeventfd->addr_len; |
| 1354 | + kioeventfd->vq = ioeventfd->vq; |
| 1355 | + |
| 1356 | + mutex_lock(&ioreq_lock); |
| 1357 | + kioreq = get_ioreq(ioeventfd, kioeventfd->eventfd); |
| 1358 | + if (IS_ERR(kioreq)) { |
| 1359 | + mutex_unlock(&ioreq_lock); |
| 1360 | + ret = PTR_ERR(kioreq); |
| 1361 | + goto error_eventfd; |
| 1362 | + } |
| 1363 | + |
| 1364 | + spin_lock_irqsave(&kioreq->lock, flags); |
| 1365 | + list_add_tail(&kioeventfd->list, &kioreq->ioeventfds); |
| 1366 | + spin_unlock_irqrestore(&kioreq->lock, flags); |
| 1367 | + |
| 1368 | + mutex_unlock(&ioreq_lock); |
| 1369 | + |
| 1370 | + return 0; |
| 1371 | + |
| 1372 | +error_eventfd: |
| 1373 | + eventfd_ctx_put(kioeventfd->eventfd); |
| 1374 | + |
| 1375 | +error_kfree: |
| 1376 | + kfree(kioeventfd); |
| 1377 | + return ret; |
| 1378 | +} |
| 1379 | + |
| 1380 | +static int privcmd_ioeventfd_deassign(struct privcmd_ioeventfd *ioeventfd) |
| 1381 | +{ |
| 1382 | + struct privcmd_kernel_ioreq *kioreq, *tkioreq; |
| 1383 | + struct eventfd_ctx *eventfd; |
| 1384 | + unsigned long flags; |
| 1385 | + int ret = 0; |
| 1386 | + |
| 1387 | + eventfd = eventfd_ctx_fdget(ioeventfd->event_fd); |
| 1388 | + if (IS_ERR(eventfd)) |
| 1389 | + return PTR_ERR(eventfd); |
| 1390 | + |
| 1391 | + mutex_lock(&ioreq_lock); |
| 1392 | + list_for_each_entry_safe(kioreq, tkioreq, &ioreq_list, list) { |
| 1393 | + struct privcmd_kernel_ioeventfd *kioeventfd, *tmp; |
| 1394 | + /* |
| 1395 | + * kioreq fields can be accessed here without a lock as they are |
| 1396 | + * never updated after being added to the ioreq_list. |
| 1397 | + */ |
| 1398 | + if (kioreq->dom != ioeventfd->dom || |
| 1399 | + kioreq->uioreq != ioeventfd->ioreq || |
| 1400 | + kioreq->vcpus != ioeventfd->vcpus) |
| 1401 | + continue; |
| 1402 | + |
| 1403 | + spin_lock_irqsave(&kioreq->lock, flags); |
| 1404 | + list_for_each_entry_safe(kioeventfd, tmp, &kioreq->ioeventfds, list) { |
| 1405 | + if (eventfd == kioeventfd->eventfd) { |
| 1406 | + ioeventfd_free(kioeventfd); |
| 1407 | + spin_unlock_irqrestore(&kioreq->lock, flags); |
| 1408 | + |
| 1409 | + if (list_empty(&kioreq->ioeventfds)) |
| 1410 | + ioreq_free(kioreq); |
| 1411 | + goto unlock; |
| 1412 | + } |
| 1413 | + } |
| 1414 | + spin_unlock_irqrestore(&kioreq->lock, flags); |
| 1415 | + break; |
| 1416 | + } |
| 1417 | + |
| 1418 | + pr_err("Ioeventfd isn't already assigned, dom: %u, addr: %llu\n", |
| 1419 | + ioeventfd->dom, ioeventfd->addr); |
| 1420 | + ret = -ENODEV; |
| 1421 | + |
| 1422 | +unlock: |
| 1423 | + mutex_unlock(&ioreq_lock); |
| 1424 | + eventfd_ctx_put(eventfd); |
| 1425 | + |
| 1426 | + return ret; |
| 1427 | +} |
| 1428 | + |
| 1429 | +static long privcmd_ioctl_ioeventfd(struct file *file, void __user *udata) |
| 1430 | +{ |
| 1431 | + struct privcmd_data *data = file->private_data; |
| 1432 | + struct privcmd_ioeventfd ioeventfd; |
| 1433 | + |
| 1434 | + if (copy_from_user(&ioeventfd, udata, sizeof(ioeventfd))) |
| 1435 | + return -EFAULT; |
| 1436 | + |
| 1437 | + /* No other flags should be set */ |
| 1438 | + if (ioeventfd.flags & ~PRIVCMD_IOEVENTFD_FLAG_DEASSIGN) |
| 1439 | + return -EINVAL; |
| 1440 | + |
| 1441 | + /* If restriction is in place, check the domid matches */ |
| 1442 | + if (data->domid != DOMID_INVALID && data->domid != ioeventfd.dom) |
| 1443 | + return -EPERM; |
| 1444 | + |
| 1445 | + if (ioeventfd.flags & PRIVCMD_IOEVENTFD_FLAG_DEASSIGN) |
| 1446 | + return privcmd_ioeventfd_deassign(&ioeventfd); |
| 1447 | + |
| 1448 | + return privcmd_ioeventfd_assign(&ioeventfd); |
| 1449 | +} |
| 1450 | + |
| 1451 | +static void privcmd_ioeventfd_exit(void) |
| 1452 | +{ |
| 1453 | + struct privcmd_kernel_ioreq *kioreq, *tmp; |
| 1454 | + unsigned long flags; |
| 1455 | + |
| 1456 | + mutex_lock(&ioreq_lock); |
| 1457 | + list_for_each_entry_safe(kioreq, tmp, &ioreq_list, list) { |
| 1458 | + struct privcmd_kernel_ioeventfd *kioeventfd, *tmp; |
| 1459 | + |
| 1460 | + spin_lock_irqsave(&kioreq->lock, flags); |
| 1461 | + list_for_each_entry_safe(kioeventfd, tmp, &kioreq->ioeventfds, list) |
| 1462 | + ioeventfd_free(kioeventfd); |
| 1463 | + spin_unlock_irqrestore(&kioreq->lock, flags); |
| 1464 | + |
| 1465 | + ioreq_free(kioreq); |
| 1466 | + } |
| 1467 | + mutex_unlock(&ioreq_lock); |
| 1468 | +} |
| 1082 | 1469 | #else |
| 1083 | 1470 | static inline long privcmd_ioctl_irqfd(struct file *file, void __user *udata) |
| 1084 | 1471 | { |
| @@ -1093,7 +1480,16 @@ static inline int privcmd_irqfd_init(void) · 8 unmodified lines |
| 1093 | 1480 | static inline void privcmd_irqfd_exit(void) |
| 1094 | 1481 | { |
| 1095 | 1482 | } |
| 1096 | | −#endif /* CONFIG_XEN_PRIVCMD_IRQFD */ |
| 1483 | + |
| 1484 | +static inline long privcmd_ioctl_ioeventfd(struct file *file, void __user *udata) |
| 1485 | +{ |
| 1486 | + return -EOPNOTSUPP; |
| 1487 | +} |
| 1488 | + |
| 1489 | +static inline void privcmd_ioeventfd_exit(void) |
| 1490 | +{ |
| 1491 | +} |
| 1492 | +#endif /* CONFIG_XEN_PRIVCMD_EVENTFD */ |
| 1097 | 1493 | |
| 1098 | 1494 | static long privcmd_ioctl(struct file *file, |
| 1099 | 1495 | unsigned int cmd, unsigned long data) |
| @@ -1134,6 +1530,10 @@ static long privcmd_ioctl(struct file *file, · 34 unmodified lines |
| 1134 | 1530 | ret = privcmd_ioctl_irqfd(file, udata); |
| 1135 | 1531 | break; |
| 1136 | 1532 | |
| 1533 | + case IOCTL_PRIVCMD_IOEVENTFD: |
| 1534 | + ret = privcmd_ioctl_ioeventfd(file, udata); |
| 1535 | + break; |
| 1536 | + |
| 1137 | 1537 | default: |
| 1138 | 1538 | break; |
| 1139 | 1539 | } |
| @@ -1278,6 +1678,7 @@ err_privcmdbuf: · 138 unmodified lines |
| 1278 | 1678 | |
| 1279 | 1679 | static void __exit privcmd_exit(void) |
| 1280 | 1680 | { |
| 1681 | + privcmd_ioeventfd_exit(); |
| 1281 | 1682 | privcmd_irqfd_exit(); |
| 1282 | 1683 | misc_deregister(&privcmd_dev); |
| 1283 | 1684 | misc_deregister(&xen_privcmdbuf_dev); |