diff options
Diffstat (limited to 'virt/kvm/kvm_main.c')
-rw-r--r-- | virt/kvm/kvm_main.c | 112 |
1 files changed, 100 insertions, 12 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index aefdda390f5e..d9cfb782cb81 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -47,6 +47,8 @@ | |||
47 | #include <linux/srcu.h> | 47 | #include <linux/srcu.h> |
48 | #include <linux/hugetlb.h> | 48 | #include <linux/hugetlb.h> |
49 | #include <linux/slab.h> | 49 | #include <linux/slab.h> |
50 | #include <linux/sort.h> | ||
51 | #include <linux/bsearch.h> | ||
50 | 52 | ||
51 | #include <asm/processor.h> | 53 | #include <asm/processor.h> |
52 | #include <asm/io.h> | 54 | #include <asm/io.h> |
@@ -2391,24 +2393,92 @@ static void kvm_io_bus_destroy(struct kvm_io_bus *bus) | |||
2391 | int i; | 2393 | int i; |
2392 | 2394 | ||
2393 | for (i = 0; i < bus->dev_count; i++) { | 2395 | for (i = 0; i < bus->dev_count; i++) { |
2394 | struct kvm_io_device *pos = bus->devs[i]; | 2396 | struct kvm_io_device *pos = bus->range[i].dev; |
2395 | 2397 | ||
2396 | kvm_iodevice_destructor(pos); | 2398 | kvm_iodevice_destructor(pos); |
2397 | } | 2399 | } |
2398 | kfree(bus); | 2400 | kfree(bus); |
2399 | } | 2401 | } |
2400 | 2402 | ||
2403 | int kvm_io_bus_sort_cmp(const void *p1, const void *p2) | ||
2404 | { | ||
2405 | const struct kvm_io_range *r1 = p1; | ||
2406 | const struct kvm_io_range *r2 = p2; | ||
2407 | |||
2408 | if (r1->addr < r2->addr) | ||
2409 | return -1; | ||
2410 | if (r1->addr + r1->len > r2->addr + r2->len) | ||
2411 | return 1; | ||
2412 | return 0; | ||
2413 | } | ||
2414 | |||
2415 | int kvm_io_bus_insert_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev, | ||
2416 | gpa_t addr, int len) | ||
2417 | { | ||
2418 | if (bus->dev_count == NR_IOBUS_DEVS) | ||
2419 | return -ENOSPC; | ||
2420 | |||
2421 | bus->range[bus->dev_count++] = (struct kvm_io_range) { | ||
2422 | .addr = addr, | ||
2423 | .len = len, | ||
2424 | .dev = dev, | ||
2425 | }; | ||
2426 | |||
2427 | sort(bus->range, bus->dev_count, sizeof(struct kvm_io_range), | ||
2428 | kvm_io_bus_sort_cmp, NULL); | ||
2429 | |||
2430 | return 0; | ||
2431 | } | ||
2432 | |||
2433 | int kvm_io_bus_get_first_dev(struct kvm_io_bus *bus, | ||
2434 | gpa_t addr, int len) | ||
2435 | { | ||
2436 | struct kvm_io_range *range, key; | ||
2437 | int off; | ||
2438 | |||
2439 | key = (struct kvm_io_range) { | ||
2440 | .addr = addr, | ||
2441 | .len = len, | ||
2442 | }; | ||
2443 | |||
2444 | range = bsearch(&key, bus->range, bus->dev_count, | ||
2445 | sizeof(struct kvm_io_range), kvm_io_bus_sort_cmp); | ||
2446 | if (range == NULL) | ||
2447 | return -ENOENT; | ||
2448 | |||
2449 | off = range - bus->range; | ||
2450 | |||
2451 | while (off > 0 && kvm_io_bus_sort_cmp(&key, &bus->range[off-1]) == 0) | ||
2452 | off--; | ||
2453 | |||
2454 | return off; | ||
2455 | } | ||
2456 | |||
2401 | /* kvm_io_bus_write - called under kvm->slots_lock */ | 2457 | /* kvm_io_bus_write - called under kvm->slots_lock */ |
2402 | int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | 2458 | int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
2403 | int len, const void *val) | 2459 | int len, const void *val) |
2404 | { | 2460 | { |
2405 | int i; | 2461 | int idx; |
2406 | struct kvm_io_bus *bus; | 2462 | struct kvm_io_bus *bus; |
2463 | struct kvm_io_range range; | ||
2464 | |||
2465 | range = (struct kvm_io_range) { | ||
2466 | .addr = addr, | ||
2467 | .len = len, | ||
2468 | }; | ||
2407 | 2469 | ||
2408 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); | 2470 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); |
2409 | for (i = 0; i < bus->dev_count; i++) | 2471 | idx = kvm_io_bus_get_first_dev(bus, addr, len); |
2410 | if (!kvm_iodevice_write(bus->devs[i], addr, len, val)) | 2472 | if (idx < 0) |
2473 | return -EOPNOTSUPP; | ||
2474 | |||
2475 | while (idx < bus->dev_count && | ||
2476 | kvm_io_bus_sort_cmp(&range, &bus->range[idx]) == 0) { | ||
2477 | if (!kvm_iodevice_write(bus->range[idx].dev, addr, len, val)) | ||
2411 | return 0; | 2478 | return 0; |
2479 | idx++; | ||
2480 | } | ||
2481 | |||
2412 | return -EOPNOTSUPP; | 2482 | return -EOPNOTSUPP; |
2413 | } | 2483 | } |
2414 | 2484 | ||
@@ -2416,19 +2486,33 @@ int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | |||
2416 | int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | 2486 | int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
2417 | int len, void *val) | 2487 | int len, void *val) |
2418 | { | 2488 | { |
2419 | int i; | 2489 | int idx; |
2420 | struct kvm_io_bus *bus; | 2490 | struct kvm_io_bus *bus; |
2491 | struct kvm_io_range range; | ||
2492 | |||
2493 | range = (struct kvm_io_range) { | ||
2494 | .addr = addr, | ||
2495 | .len = len, | ||
2496 | }; | ||
2421 | 2497 | ||
2422 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); | 2498 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); |
2423 | for (i = 0; i < bus->dev_count; i++) | 2499 | idx = kvm_io_bus_get_first_dev(bus, addr, len); |
2424 | if (!kvm_iodevice_read(bus->devs[i], addr, len, val)) | 2500 | if (idx < 0) |
2501 | return -EOPNOTSUPP; | ||
2502 | |||
2503 | while (idx < bus->dev_count && | ||
2504 | kvm_io_bus_sort_cmp(&range, &bus->range[idx]) == 0) { | ||
2505 | if (!kvm_iodevice_read(bus->range[idx].dev, addr, len, val)) | ||
2425 | return 0; | 2506 | return 0; |
2507 | idx++; | ||
2508 | } | ||
2509 | |||
2426 | return -EOPNOTSUPP; | 2510 | return -EOPNOTSUPP; |
2427 | } | 2511 | } |
2428 | 2512 | ||
2429 | /* Caller must hold slots_lock. */ | 2513 | /* Caller must hold slots_lock. */ |
2430 | int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, | 2514 | int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
2431 | struct kvm_io_device *dev) | 2515 | int len, struct kvm_io_device *dev) |
2432 | { | 2516 | { |
2433 | struct kvm_io_bus *new_bus, *bus; | 2517 | struct kvm_io_bus *new_bus, *bus; |
2434 | 2518 | ||
@@ -2440,7 +2524,7 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, | |||
2440 | if (!new_bus) | 2524 | if (!new_bus) |
2441 | return -ENOMEM; | 2525 | return -ENOMEM; |
2442 | memcpy(new_bus, bus, sizeof(struct kvm_io_bus)); | 2526 | memcpy(new_bus, bus, sizeof(struct kvm_io_bus)); |
2443 | new_bus->devs[new_bus->dev_count++] = dev; | 2527 | kvm_io_bus_insert_dev(new_bus, dev, addr, len); |
2444 | rcu_assign_pointer(kvm->buses[bus_idx], new_bus); | 2528 | rcu_assign_pointer(kvm->buses[bus_idx], new_bus); |
2445 | synchronize_srcu_expedited(&kvm->srcu); | 2529 | synchronize_srcu_expedited(&kvm->srcu); |
2446 | kfree(bus); | 2530 | kfree(bus); |
@@ -2464,9 +2548,13 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, | |||
2464 | 2548 | ||
2465 | r = -ENOENT; | 2549 | r = -ENOENT; |
2466 | for (i = 0; i < new_bus->dev_count; i++) | 2550 | for (i = 0; i < new_bus->dev_count; i++) |
2467 | if (new_bus->devs[i] == dev) { | 2551 | if (new_bus->range[i].dev == dev) { |
2468 | r = 0; | 2552 | r = 0; |
2469 | new_bus->devs[i] = new_bus->devs[--new_bus->dev_count]; | 2553 | new_bus->dev_count--; |
2554 | new_bus->range[i] = new_bus->range[new_bus->dev_count]; | ||
2555 | sort(new_bus->range, new_bus->dev_count, | ||
2556 | sizeof(struct kvm_io_range), | ||
2557 | kvm_io_bus_sort_cmp, NULL); | ||
2470 | break; | 2558 | break; |
2471 | } | 2559 | } |
2472 | 2560 | ||