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

Configure Feed

Select the types of activity you want to include in your feed.

Drivers: hv: Support establishing the confidential VMBus connection

To establish the confidential VMBus connection the CoCo VM, the guest
first checks on the confidential VMBus availability, and then proceeds
to initializing the communication stack.

Implement that in the VMBus driver initialization.

Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Roman Kisel and committed by
Wei Liu
2647c966 b537794b

+114 -70
+114 -70
drivers/hv/vmbus_drv.c
··· 1057 1057 kfree(ctx); 1058 1058 } 1059 1059 1060 - void vmbus_on_msg_dpc(unsigned long data) 1060 + static void __vmbus_on_msg_dpc(void *message_page_addr) 1061 1061 { 1062 - struct hv_per_cpu_context *hv_cpu = (void *)data; 1063 - void *page_addr = hv_cpu->hyp_synic_message_page; 1064 - struct hv_message msg_copy, *msg = (struct hv_message *)page_addr + 1065 - VMBUS_MESSAGE_SINT; 1062 + struct hv_message msg_copy, *msg; 1066 1063 struct vmbus_channel_message_header *hdr; 1067 1064 enum vmbus_channel_message_type msgtype; 1068 1065 const struct vmbus_channel_message_table_entry *entry; 1069 1066 struct onmessage_work_context *ctx; 1070 1067 __u8 payload_size; 1071 1068 u32 message_type; 1069 + 1070 + if (!message_page_addr) 1071 + return; 1072 + msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT; 1072 1073 1073 1074 /* 1074 1075 * 'enum vmbus_channel_message_type' is supposed to always be 'u32' as ··· 1196 1195 vmbus_signal_eom(msg, message_type); 1197 1196 } 1198 1197 1198 + void vmbus_on_msg_dpc(unsigned long data) 1199 + { 1200 + struct hv_per_cpu_context *hv_cpu = (void *)data; 1201 + 1202 + __vmbus_on_msg_dpc(hv_cpu->hyp_synic_message_page); 1203 + __vmbus_on_msg_dpc(hv_cpu->para_synic_message_page); 1204 + } 1205 + 1199 1206 #ifdef CONFIG_PM_SLEEP 1200 1207 /* 1201 1208 * Fake RESCIND_CHANNEL messages to clean up hv_sock channels by force for ··· 1242 1233 #endif /* CONFIG_PM_SLEEP */ 1243 1234 1244 1235 /* 1245 - * Schedule all channels with events pending 1236 + * Schedule all channels with events pending. 1237 + * The event page can be directly checked to get the id of 1238 + * the channel that has the interrupt pending. 1246 1239 */ 1247 - static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) 1240 + static void vmbus_chan_sched(void *event_page_addr) 1248 1241 { 1249 1242 unsigned long *recv_int_page; 1250 1243 u32 maxbits, relid; 1244 + union hv_synic_event_flags *event; 1251 1245 1252 - /* 1253 - * The event page can be directly checked to get the id of 1254 - * the channel that has the interrupt pending. 1255 - */ 1256 - void *page_addr = hv_cpu->hyp_synic_event_page; 1257 - union hv_synic_event_flags *event 1258 - = (union hv_synic_event_flags *)page_addr + 1259 - VMBUS_MESSAGE_SINT; 1246 + if (!event_page_addr) 1247 + return; 1248 + event = (union hv_synic_event_flags *)event_page_addr + VMBUS_MESSAGE_SINT; 1260 1249 1261 1250 maxbits = HV_EVENT_FLAGS_COUNT; 1262 1251 recv_int_page = event->flags; ··· 1262 1255 if (unlikely(!recv_int_page)) 1263 1256 return; 1264 1257 1258 + /* 1259 + * Suggested-by: Michael Kelley <mhklinux@outlook.com> 1260 + * One possible optimization would be to keep track of the largest relID that's in use, 1261 + * and only scan up to that relID. 1262 + */ 1265 1263 for_each_set_bit(relid, recv_int_page, maxbits) { 1266 1264 void (*callback_fn)(void *context); 1267 1265 struct vmbus_channel *channel; ··· 1330 1318 } 1331 1319 } 1332 1320 1333 - static void vmbus_isr(void) 1321 + static void vmbus_message_sched(struct hv_per_cpu_context *hv_cpu, void *message_page_addr) 1334 1322 { 1335 - struct hv_per_cpu_context *hv_cpu 1336 - = this_cpu_ptr(hv_context.cpu_context); 1337 - void *page_addr; 1338 1323 struct hv_message *msg; 1339 1324 1340 - vmbus_chan_sched(hv_cpu); 1341 - 1342 - page_addr = hv_cpu->hyp_synic_message_page; 1343 - msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 1325 + if (!message_page_addr) 1326 + return; 1327 + msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT; 1344 1328 1345 1329 /* Check if there are actual msgs to be processed */ 1346 1330 if (msg->header.message_type != HVMSG_NONE) { 1347 1331 if (msg->header.message_type == HVMSG_TIMER_EXPIRED) { 1348 1332 hv_stimer0_isr(); 1349 1333 vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED); 1350 - } else 1334 + } else { 1351 1335 tasklet_schedule(&hv_cpu->msg_dpc); 1336 + } 1352 1337 } 1338 + } 1339 + 1340 + static void vmbus_isr(void) 1341 + { 1342 + struct hv_per_cpu_context *hv_cpu 1343 + = this_cpu_ptr(hv_context.cpu_context); 1344 + 1345 + vmbus_chan_sched(hv_cpu->hyp_synic_event_page); 1346 + vmbus_chan_sched(hv_cpu->para_synic_event_page); 1347 + 1348 + vmbus_message_sched(hv_cpu, hv_cpu->hyp_synic_message_page); 1349 + vmbus_message_sched(hv_cpu, hv_cpu->para_synic_message_page); 1353 1350 1354 1351 add_interrupt_randomness(vmbus_interrupt); 1355 1352 } ··· 1376 1355 hv_synic_init(cpu); 1377 1356 } 1378 1357 1379 - /* 1380 - * vmbus_bus_init -Main vmbus driver initialization routine. 1381 - * 1382 - * Here, we 1383 - * - initialize the vmbus driver context 1384 - * - invoke the vmbus hv main init routine 1385 - * - retrieve the channel offers 1386 - */ 1387 - static int vmbus_bus_init(void) 1358 + static int vmbus_alloc_synic_and_connect(void) 1388 1359 { 1389 1360 int ret, cpu; 1390 1361 struct work_struct __percpu *works; 1391 - 1392 - ret = hv_init(); 1393 - if (ret != 0) { 1394 - pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); 1395 - return ret; 1396 - } 1397 - 1398 - ret = bus_register(&hv_bus); 1399 - if (ret) 1400 - return ret; 1401 - 1402 - /* 1403 - * VMbus interrupts are best modeled as per-cpu interrupts. If 1404 - * on an architecture with support for per-cpu IRQs (e.g. ARM64), 1405 - * allocate a per-cpu IRQ using standard Linux kernel functionality. 1406 - * If not on such an architecture (e.g., x86/x64), then rely on 1407 - * code in the arch-specific portion of the code tree to connect 1408 - * the VMbus interrupt handler. 1409 - */ 1410 - 1411 - if (vmbus_irq == -1) { 1412 - hv_setup_vmbus_handler(vmbus_isr); 1413 - } else { 1414 - vmbus_evt = alloc_percpu(long); 1415 - ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr, 1416 - "Hyper-V VMbus", vmbus_evt); 1417 - if (ret) { 1418 - pr_err("Can't request Hyper-V VMbus IRQ %d, Err %d", 1419 - vmbus_irq, ret); 1420 - free_percpu(vmbus_evt); 1421 - goto err_setup; 1422 - } 1423 - } 1362 + int hyperv_cpuhp_online; 1424 1363 1425 1364 ret = hv_synic_alloc(); 1426 - if (ret) 1365 + if (ret < 0) 1427 1366 goto err_alloc; 1428 1367 1429 1368 works = alloc_percpu(struct work_struct); ··· 1419 1438 ret = vmbus_connect(); 1420 1439 if (ret) 1421 1440 goto err_connect; 1441 + return 0; 1442 + 1443 + err_connect: 1444 + cpuhp_remove_state(hyperv_cpuhp_online); 1445 + return -ENODEV; 1446 + err_alloc: 1447 + hv_synic_free(); 1448 + return -ENOMEM; 1449 + } 1450 + 1451 + /* 1452 + * vmbus_bus_init -Main vmbus driver initialization routine. 1453 + * 1454 + * Here, we 1455 + * - initialize the vmbus driver context 1456 + * - invoke the vmbus hv main init routine 1457 + * - retrieve the channel offers 1458 + */ 1459 + static int vmbus_bus_init(void) 1460 + { 1461 + int ret; 1462 + 1463 + ret = hv_init(); 1464 + if (ret != 0) { 1465 + pr_err("Unable to initialize the hypervisor - 0x%x\n", ret); 1466 + return ret; 1467 + } 1468 + 1469 + ret = bus_register(&hv_bus); 1470 + if (ret) 1471 + return ret; 1472 + 1473 + /* 1474 + * VMbus interrupts are best modeled as per-cpu interrupts. If 1475 + * on an architecture with support for per-cpu IRQs (e.g. ARM64), 1476 + * allocate a per-cpu IRQ using standard Linux kernel functionality. 1477 + * If not on such an architecture (e.g., x86/x64), then rely on 1478 + * code in the arch-specific portion of the code tree to connect 1479 + * the VMbus interrupt handler. 1480 + */ 1481 + 1482 + if (vmbus_irq == -1) { 1483 + hv_setup_vmbus_handler(vmbus_isr); 1484 + } else { 1485 + vmbus_evt = alloc_percpu(long); 1486 + ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr, 1487 + "Hyper-V VMbus", vmbus_evt); 1488 + if (ret) { 1489 + pr_err("Can't request Hyper-V VMbus IRQ %d, Err %d", 1490 + vmbus_irq, ret); 1491 + free_percpu(vmbus_evt); 1492 + goto err_setup; 1493 + } 1494 + } 1495 + 1496 + /* 1497 + * Cache the value as getting it involves a VM exit on x86(_64), and 1498 + * doing that on each VP while initializing SynIC's wastes time. 1499 + */ 1500 + is_confidential = ms_hyperv.confidential_vmbus_available; 1501 + if (is_confidential) 1502 + pr_info("Establishing connection to the confidential VMBus\n"); 1503 + hv_para_set_sint_proxy(!is_confidential); 1504 + ret = vmbus_alloc_synic_and_connect(); 1505 + if (ret) 1506 + goto err_connect; 1422 1507 1423 1508 /* 1424 1509 * Always register the vmbus unload panic notifier because we ··· 1498 1451 return 0; 1499 1452 1500 1453 err_connect: 1501 - cpuhp_remove_state(hyperv_cpuhp_online); 1502 - err_alloc: 1503 - hv_synic_free(); 1504 1454 if (vmbus_irq == -1) { 1505 1455 hv_remove_vmbus_handler(); 1506 1456 } else {