diff options
-rw-r--r-- | drivers/net/xen-netback/common.h | 2 | ||||
-rw-r--r-- | drivers/net/xen-netback/interface.c | 23 | ||||
-rw-r--r-- | drivers/net/xen-netback/netback.c | 8 | ||||
-rw-r--r-- | drivers/net/xen-netback/xenbus.c | 101 |
4 files changed, 116 insertions, 18 deletions
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index b6885cfcc7df..4dd7c4a1923b 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h | |||
@@ -237,6 +237,7 @@ struct xenvif *xenvif_alloc(struct device *parent, | |||
237 | unsigned int handle); | 237 | unsigned int handle); |
238 | 238 | ||
239 | int xenvif_init_queue(struct xenvif_queue *queue); | 239 | int xenvif_init_queue(struct xenvif_queue *queue); |
240 | void xenvif_deinit_queue(struct xenvif_queue *queue); | ||
240 | 241 | ||
241 | int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref, | 242 | int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref, |
242 | unsigned long rx_ring_ref, unsigned int tx_evtchn, | 243 | unsigned long rx_ring_ref, unsigned int tx_evtchn, |
@@ -299,5 +300,6 @@ extern bool separate_tx_rx_irq; | |||
299 | 300 | ||
300 | extern unsigned int rx_drain_timeout_msecs; | 301 | extern unsigned int rx_drain_timeout_msecs; |
301 | extern unsigned int rx_drain_timeout_jiffies; | 302 | extern unsigned int rx_drain_timeout_jiffies; |
303 | extern unsigned int xenvif_max_queues; | ||
302 | 304 | ||
303 | #endif /* __XEN_NETBACK__COMMON_H__ */ | 305 | #endif /* __XEN_NETBACK__COMMON_H__ */ |
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 6005b5d1d404..6929bcb61cf0 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c | |||
@@ -139,7 +139,6 @@ static void xenvif_wake_queue_callback(unsigned long data) | |||
139 | static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb, | 139 | static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb, |
140 | void *accel_priv, select_queue_fallback_t fallback) | 140 | void *accel_priv, select_queue_fallback_t fallback) |
141 | { | 141 | { |
142 | struct xenvif *vif = netdev_priv(dev); | ||
143 | unsigned int num_queues = dev->real_num_tx_queues; | 142 | unsigned int num_queues = dev->real_num_tx_queues; |
144 | u32 hash; | 143 | u32 hash; |
145 | u16 queue_index; | 144 | u16 queue_index; |
@@ -436,7 +435,12 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, | |||
436 | char name[IFNAMSIZ] = {}; | 435 | char name[IFNAMSIZ] = {}; |
437 | 436 | ||
438 | snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle); | 437 | snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle); |
439 | dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup, 1); | 438 | /* Allocate a netdev with the max. supported number of queues. |
439 | * When the guest selects the desired number, it will be updated | ||
440 | * via netif_set_real_num_tx_queues(). | ||
441 | */ | ||
442 | dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup, | ||
443 | xenvif_max_queues); | ||
440 | if (dev == NULL) { | 444 | if (dev == NULL) { |
441 | pr_warn("Could not allocate netdev for %s\n", name); | 445 | pr_warn("Could not allocate netdev for %s\n", name); |
442 | return ERR_PTR(-ENOMEM); | 446 | return ERR_PTR(-ENOMEM); |
@@ -706,6 +710,16 @@ void xenvif_disconnect(struct xenvif *vif) | |||
706 | } | 710 | } |
707 | } | 711 | } |
708 | 712 | ||
713 | /* Reverse the relevant parts of xenvif_init_queue(). | ||
714 | * Used for queue teardown from xenvif_free(), and on the | ||
715 | * error handling paths in xenbus.c:connect(). | ||
716 | */ | ||
717 | void xenvif_deinit_queue(struct xenvif_queue *queue) | ||
718 | { | ||
719 | free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages); | ||
720 | netif_napi_del(&queue->napi); | ||
721 | } | ||
722 | |||
709 | void xenvif_free(struct xenvif *vif) | 723 | void xenvif_free(struct xenvif *vif) |
710 | { | 724 | { |
711 | struct xenvif_queue *queue = NULL; | 725 | struct xenvif_queue *queue = NULL; |
@@ -729,11 +743,8 @@ void xenvif_free(struct xenvif *vif) | |||
729 | 743 | ||
730 | for (queue_index = 0; queue_index < num_queues; ++queue_index) { | 744 | for (queue_index = 0; queue_index < num_queues; ++queue_index) { |
731 | queue = &vif->queues[queue_index]; | 745 | queue = &vif->queues[queue_index]; |
732 | |||
733 | xenvif_wait_unmap_timeout(queue, worst_case_skb_lifetime); | 746 | xenvif_wait_unmap_timeout(queue, worst_case_skb_lifetime); |
734 | free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages); | 747 | xenvif_deinit_queue(queue); |
735 | |||
736 | netif_napi_del(&queue->napi); | ||
737 | } | 748 | } |
738 | 749 | ||
739 | /* Free the array of queues. The call below does not require | 750 | /* Free the array of queues. The call below does not require |
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index a5484e8cb06e..49efff9b99f4 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c | |||
@@ -62,6 +62,11 @@ unsigned int rx_drain_timeout_msecs = 10000; | |||
62 | module_param(rx_drain_timeout_msecs, uint, 0444); | 62 | module_param(rx_drain_timeout_msecs, uint, 0444); |
63 | unsigned int rx_drain_timeout_jiffies; | 63 | unsigned int rx_drain_timeout_jiffies; |
64 | 64 | ||
65 | unsigned int xenvif_max_queues; | ||
66 | module_param_named(max_queues, xenvif_max_queues, uint, 0644); | ||
67 | MODULE_PARM_DESC(max_queues, | ||
68 | "Maximum number of queues per virtual interface"); | ||
69 | |||
65 | /* | 70 | /* |
66 | * This is the maximum slots a skb can have. If a guest sends a skb | 71 | * This is the maximum slots a skb can have. If a guest sends a skb |
67 | * which exceeds this limit it is considered malicious. | 72 | * which exceeds this limit it is considered malicious. |
@@ -1953,6 +1958,9 @@ static int __init netback_init(void) | |||
1953 | if (!xen_domain()) | 1958 | if (!xen_domain()) |
1954 | return -ENODEV; | 1959 | return -ENODEV; |
1955 | 1960 | ||
1961 | /* Allow as many queues as there are CPUs, by default */ | ||
1962 | xenvif_max_queues = num_online_cpus(); | ||
1963 | |||
1956 | if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) { | 1964 | if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) { |
1957 | pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", | 1965 | pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", |
1958 | fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX); | 1966 | fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX); |
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 358602f55afa..96c63dc2509e 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c | |||
@@ -160,6 +160,12 @@ static int netback_probe(struct xenbus_device *dev, | |||
160 | if (err) | 160 | if (err) |
161 | pr_debug("Error writing feature-split-event-channels\n"); | 161 | pr_debug("Error writing feature-split-event-channels\n"); |
162 | 162 | ||
163 | /* Multi-queue support: This is an optional feature. */ | ||
164 | err = xenbus_printf(XBT_NIL, dev->nodename, | ||
165 | "multi-queue-max-queues", "%u", xenvif_max_queues); | ||
166 | if (err) | ||
167 | pr_debug("Error writing multi-queue-max-queues\n"); | ||
168 | |||
163 | err = xenbus_switch_state(dev, XenbusStateInitWait); | 169 | err = xenbus_switch_state(dev, XenbusStateInitWait); |
164 | if (err) | 170 | if (err) |
165 | goto fail; | 171 | goto fail; |
@@ -490,9 +496,25 @@ static void connect(struct backend_info *be) | |||
490 | struct xenbus_device *dev = be->dev; | 496 | struct xenbus_device *dev = be->dev; |
491 | unsigned long credit_bytes, credit_usec; | 497 | unsigned long credit_bytes, credit_usec; |
492 | unsigned int queue_index; | 498 | unsigned int queue_index; |
493 | unsigned int requested_num_queues = 1; | 499 | unsigned int requested_num_queues; |
494 | struct xenvif_queue *queue; | 500 | struct xenvif_queue *queue; |
495 | 501 | ||
502 | /* Check whether the frontend requested multiple queues | ||
503 | * and read the number requested. | ||
504 | */ | ||
505 | err = xenbus_scanf(XBT_NIL, dev->otherend, | ||
506 | "multi-queue-num-queues", | ||
507 | "%u", &requested_num_queues); | ||
508 | if (err < 0) { | ||
509 | requested_num_queues = 1; /* Fall back to single queue */ | ||
510 | } else if (requested_num_queues > xenvif_max_queues) { | ||
511 | /* buggy or malicious guest */ | ||
512 | xenbus_dev_fatal(dev, err, | ||
513 | "guest requested %u queues, exceeding the maximum of %u.", | ||
514 | requested_num_queues, xenvif_max_queues); | ||
515 | return; | ||
516 | } | ||
517 | |||
496 | err = xen_net_read_mac(dev, be->vif->fe_dev_addr); | 518 | err = xen_net_read_mac(dev, be->vif->fe_dev_addr); |
497 | if (err) { | 519 | if (err) { |
498 | xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); | 520 | xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); |
@@ -502,6 +524,7 @@ static void connect(struct backend_info *be) | |||
502 | xen_net_read_rate(dev, &credit_bytes, &credit_usec); | 524 | xen_net_read_rate(dev, &credit_bytes, &credit_usec); |
503 | read_xenbus_vif_flags(be); | 525 | read_xenbus_vif_flags(be); |
504 | 526 | ||
527 | /* Use the number of queues requested by the frontend */ | ||
505 | be->vif->queues = vzalloc(requested_num_queues * | 528 | be->vif->queues = vzalloc(requested_num_queues * |
506 | sizeof(struct xenvif_queue)); | 529 | sizeof(struct xenvif_queue)); |
507 | rtnl_lock(); | 530 | rtnl_lock(); |
@@ -516,14 +539,33 @@ static void connect(struct backend_info *be) | |||
516 | be->vif->dev->name, queue->id); | 539 | be->vif->dev->name, queue->id); |
517 | 540 | ||
518 | err = xenvif_init_queue(queue); | 541 | err = xenvif_init_queue(queue); |
519 | if (err) | 542 | if (err) { |
543 | /* xenvif_init_queue() cleans up after itself on | ||
544 | * failure, but we need to clean up any previously | ||
545 | * initialised queues. Set num_queues to i so that | ||
546 | * earlier queues can be destroyed using the regular | ||
547 | * disconnect logic. | ||
548 | */ | ||
549 | rtnl_lock(); | ||
550 | netif_set_real_num_tx_queues(be->vif->dev, queue_index); | ||
551 | rtnl_unlock(); | ||
520 | goto err; | 552 | goto err; |
553 | } | ||
521 | 554 | ||
522 | queue->remaining_credit = credit_bytes; | 555 | queue->remaining_credit = credit_bytes; |
523 | 556 | ||
524 | err = connect_rings(be, queue); | 557 | err = connect_rings(be, queue); |
525 | if (err) | 558 | if (err) { |
559 | /* connect_rings() cleans up after itself on failure, | ||
560 | * but we need to clean up after xenvif_init_queue() here, | ||
561 | * and also clean up any previously initialised queues. | ||
562 | */ | ||
563 | xenvif_deinit_queue(queue); | ||
564 | rtnl_lock(); | ||
565 | netif_set_real_num_tx_queues(be->vif->dev, queue_index); | ||
566 | rtnl_unlock(); | ||
526 | goto err; | 567 | goto err; |
568 | } | ||
527 | } | 569 | } |
528 | 570 | ||
529 | xenvif_carrier_on(be->vif); | 571 | xenvif_carrier_on(be->vif); |
@@ -540,6 +582,8 @@ static void connect(struct backend_info *be) | |||
540 | return; | 582 | return; |
541 | 583 | ||
542 | err: | 584 | err: |
585 | if (be->vif->dev->real_num_tx_queues > 0) | ||
586 | xenvif_disconnect(be->vif); /* Clean up existing queues */ | ||
543 | vfree(be->vif->queues); | 587 | vfree(be->vif->queues); |
544 | be->vif->queues = NULL; | 588 | be->vif->queues = NULL; |
545 | rtnl_lock(); | 589 | rtnl_lock(); |
@@ -552,32 +596,62 @@ err: | |||
552 | static int connect_rings(struct backend_info *be, struct xenvif_queue *queue) | 596 | static int connect_rings(struct backend_info *be, struct xenvif_queue *queue) |
553 | { | 597 | { |
554 | struct xenbus_device *dev = be->dev; | 598 | struct xenbus_device *dev = be->dev; |
599 | unsigned int num_queues = queue->vif->dev->real_num_tx_queues; | ||
555 | unsigned long tx_ring_ref, rx_ring_ref; | 600 | unsigned long tx_ring_ref, rx_ring_ref; |
556 | unsigned int tx_evtchn, rx_evtchn; | 601 | unsigned int tx_evtchn, rx_evtchn; |
557 | int err; | 602 | int err; |
603 | char *xspath; | ||
604 | size_t xspathsize; | ||
605 | const size_t xenstore_path_ext_size = 11; /* sufficient for "/queue-NNN" */ | ||
606 | |||
607 | /* If the frontend requested 1 queue, or we have fallen back | ||
608 | * to single queue due to lack of frontend support for multi- | ||
609 | * queue, expect the remaining XenStore keys in the toplevel | ||
610 | * directory. Otherwise, expect them in a subdirectory called | ||
611 | * queue-N. | ||
612 | */ | ||
613 | if (num_queues == 1) { | ||
614 | xspath = kzalloc(strlen(dev->otherend) + 1, GFP_KERNEL); | ||
615 | if (!xspath) { | ||
616 | xenbus_dev_fatal(dev, -ENOMEM, | ||
617 | "reading ring references"); | ||
618 | return -ENOMEM; | ||
619 | } | ||
620 | strcpy(xspath, dev->otherend); | ||
621 | } else { | ||
622 | xspathsize = strlen(dev->otherend) + xenstore_path_ext_size; | ||
623 | xspath = kzalloc(xspathsize, GFP_KERNEL); | ||
624 | if (!xspath) { | ||
625 | xenbus_dev_fatal(dev, -ENOMEM, | ||
626 | "reading ring references"); | ||
627 | return -ENOMEM; | ||
628 | } | ||
629 | snprintf(xspath, xspathsize, "%s/queue-%u", dev->otherend, | ||
630 | queue->id); | ||
631 | } | ||
558 | 632 | ||
559 | err = xenbus_gather(XBT_NIL, dev->otherend, | 633 | err = xenbus_gather(XBT_NIL, xspath, |
560 | "tx-ring-ref", "%lu", &tx_ring_ref, | 634 | "tx-ring-ref", "%lu", &tx_ring_ref, |
561 | "rx-ring-ref", "%lu", &rx_ring_ref, NULL); | 635 | "rx-ring-ref", "%lu", &rx_ring_ref, NULL); |
562 | if (err) { | 636 | if (err) { |
563 | xenbus_dev_fatal(dev, err, | 637 | xenbus_dev_fatal(dev, err, |
564 | "reading %s/ring-ref", | 638 | "reading %s/ring-ref", |
565 | dev->otherend); | 639 | xspath); |
566 | return err; | 640 | goto err; |
567 | } | 641 | } |
568 | 642 | ||
569 | /* Try split event channels first, then single event channel. */ | 643 | /* Try split event channels first, then single event channel. */ |
570 | err = xenbus_gather(XBT_NIL, dev->otherend, | 644 | err = xenbus_gather(XBT_NIL, xspath, |
571 | "event-channel-tx", "%u", &tx_evtchn, | 645 | "event-channel-tx", "%u", &tx_evtchn, |
572 | "event-channel-rx", "%u", &rx_evtchn, NULL); | 646 | "event-channel-rx", "%u", &rx_evtchn, NULL); |
573 | if (err < 0) { | 647 | if (err < 0) { |
574 | err = xenbus_scanf(XBT_NIL, dev->otherend, | 648 | err = xenbus_scanf(XBT_NIL, xspath, |
575 | "event-channel", "%u", &tx_evtchn); | 649 | "event-channel", "%u", &tx_evtchn); |
576 | if (err < 0) { | 650 | if (err < 0) { |
577 | xenbus_dev_fatal(dev, err, | 651 | xenbus_dev_fatal(dev, err, |
578 | "reading %s/event-channel(-tx/rx)", | 652 | "reading %s/event-channel(-tx/rx)", |
579 | dev->otherend); | 653 | xspath); |
580 | return err; | 654 | goto err; |
581 | } | 655 | } |
582 | rx_evtchn = tx_evtchn; | 656 | rx_evtchn = tx_evtchn; |
583 | } | 657 | } |
@@ -590,10 +664,13 @@ static int connect_rings(struct backend_info *be, struct xenvif_queue *queue) | |||
590 | "mapping shared-frames %lu/%lu port tx %u rx %u", | 664 | "mapping shared-frames %lu/%lu port tx %u rx %u", |
591 | tx_ring_ref, rx_ring_ref, | 665 | tx_ring_ref, rx_ring_ref, |
592 | tx_evtchn, rx_evtchn); | 666 | tx_evtchn, rx_evtchn); |
593 | return err; | 667 | goto err; |
594 | } | 668 | } |
595 | 669 | ||
596 | return 0; | 670 | err = 0; |
671 | err: /* Regular return falls through with err == 0 */ | ||
672 | kfree(xspath); | ||
673 | return err; | ||
597 | } | 674 | } |
598 | 675 | ||
599 | static int read_xenbus_vif_flags(struct backend_info *be) | 676 | static int read_xenbus_vif_flags(struct backend_info *be) |