aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrishna Kumar <krkumar2@in.ibm.com>2010-08-04 02:15:59 -0400
committerDavid S. Miller <davem@davemloft.net>2010-08-17 00:06:25 -0400
commit1565c7c1c4c8e931bdba66abc8aa6f141a406872 (patch)
tree2e2f732e113faa24f4b0e7c804b37bf4283595c3
parentbfb564e7391340638afe4ad67744a8f3858e7566 (diff)
macvtap: Implement multiqueue for macvtap driver
Implement multiqueue facility for macvtap driver. The idea is that a macvtap device can be opened multiple times and the fd's can be used to register eg, as backend for vhost. Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/macvtap.c99
-rw-r--r--include/linux/if_macvlan.h9
2 files changed, 90 insertions, 18 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 3b1c54a9c6e..42567279843 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -84,26 +84,45 @@ static const struct proto_ops macvtap_socket_ops;
84static DEFINE_SPINLOCK(macvtap_lock); 84static DEFINE_SPINLOCK(macvtap_lock);
85 85
86/* 86/*
87 * Choose the next free queue, for now there is only one 87 * get_slot: return a [unused/occupied] slot in vlan->taps[]:
88 * - if 'q' is NULL, return the first empty slot;
89 * - otherwise, return the slot this pointer occupies.
88 */ 90 */
91static int get_slot(struct macvlan_dev *vlan, struct macvtap_queue *q)
92{
93 int i;
94
95 for (i = 0; i < MAX_MACVTAP_QUEUES; i++) {
96 if (rcu_dereference(vlan->taps[i]) == q)
97 return i;
98 }
99
100 /* Should never happen */
101 BUG_ON(1);
102}
103
89static int macvtap_set_queue(struct net_device *dev, struct file *file, 104static int macvtap_set_queue(struct net_device *dev, struct file *file,
90 struct macvtap_queue *q) 105 struct macvtap_queue *q)
91{ 106{
92 struct macvlan_dev *vlan = netdev_priv(dev); 107 struct macvlan_dev *vlan = netdev_priv(dev);
108 int index;
93 int err = -EBUSY; 109 int err = -EBUSY;
94 110
95 spin_lock(&macvtap_lock); 111 spin_lock(&macvtap_lock);
96 if (rcu_dereference(vlan->tap)) 112 if (vlan->numvtaps == MAX_MACVTAP_QUEUES)
97 goto out; 113 goto out;
98 114
99 err = 0; 115 err = 0;
116 index = get_slot(vlan, NULL);
100 rcu_assign_pointer(q->vlan, vlan); 117 rcu_assign_pointer(q->vlan, vlan);
101 rcu_assign_pointer(vlan->tap, q); 118 rcu_assign_pointer(vlan->taps[index], q);
102 sock_hold(&q->sk); 119 sock_hold(&q->sk);
103 120
104 q->file = file; 121 q->file = file;
105 file->private_data = q; 122 file->private_data = q;
106 123
124 vlan->numvtaps++;
125
107out: 126out:
108 spin_unlock(&macvtap_lock); 127 spin_unlock(&macvtap_lock);
109 return err; 128 return err;
@@ -124,9 +143,12 @@ static void macvtap_put_queue(struct macvtap_queue *q)
124 spin_lock(&macvtap_lock); 143 spin_lock(&macvtap_lock);
125 vlan = rcu_dereference(q->vlan); 144 vlan = rcu_dereference(q->vlan);
126 if (vlan) { 145 if (vlan) {
127 rcu_assign_pointer(vlan->tap, NULL); 146 int index = get_slot(vlan, q);
147
148 rcu_assign_pointer(vlan->taps[index], NULL);
128 rcu_assign_pointer(q->vlan, NULL); 149 rcu_assign_pointer(q->vlan, NULL);
129 sock_put(&q->sk); 150 sock_put(&q->sk);
151 --vlan->numvtaps;
130 } 152 }
131 153
132 spin_unlock(&macvtap_lock); 154 spin_unlock(&macvtap_lock);
@@ -136,39 +158,82 @@ static void macvtap_put_queue(struct macvtap_queue *q)
136} 158}
137 159
138/* 160/*
139 * Since we only support one queue, just dereference the pointer. 161 * Select a queue based on the rxq of the device on which this packet
162 * arrived. If the incoming device is not mq, calculate a flow hash
163 * to select a queue. If all fails, find the first available queue.
164 * Cache vlan->numvtaps since it can become zero during the execution
165 * of this function.
140 */ 166 */
141static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, 167static struct macvtap_queue *macvtap_get_queue(struct net_device *dev,
142 struct sk_buff *skb) 168 struct sk_buff *skb)
143{ 169{
144 struct macvlan_dev *vlan = netdev_priv(dev); 170 struct macvlan_dev *vlan = netdev_priv(dev);
171 struct macvtap_queue *tap = NULL;
172 int numvtaps = vlan->numvtaps;
173 __u32 rxq;
174
175 if (!numvtaps)
176 goto out;
177
178 if (likely(skb_rx_queue_recorded(skb))) {
179 rxq = skb_get_rx_queue(skb);
180
181 while (unlikely(rxq >= numvtaps))
182 rxq -= numvtaps;
183
184 tap = rcu_dereference(vlan->taps[rxq]);
185 if (tap)
186 goto out;
187 }
188
189 /* Check if we can use flow to select a queue */
190 rxq = skb_get_rxhash(skb);
191 if (rxq) {
192 tap = rcu_dereference(vlan->taps[rxq % numvtaps]);
193 if (tap)
194 goto out;
195 }
145 196
146 return rcu_dereference(vlan->tap); 197 /* Everything failed - find first available queue */
198 for (rxq = 0; rxq < MAX_MACVTAP_QUEUES; rxq++) {
199 tap = rcu_dereference(vlan->taps[rxq]);
200 if (tap)
201 break;
202 }
203
204out:
205 return tap;
147} 206}
148 207
149/* 208/*
150 * The net_device is going away, give up the reference 209 * The net_device is going away, give up the reference
151 * that it holds on the queue (all the queues one day) 210 * that it holds on all queues and safely set the pointer
152 * and safely set the pointer from the queues to NULL. 211 * from the queues to NULL.
153 */ 212 */
154static void macvtap_del_queues(struct net_device *dev) 213static void macvtap_del_queues(struct net_device *dev)
155{ 214{
156 struct macvlan_dev *vlan = netdev_priv(dev); 215 struct macvlan_dev *vlan = netdev_priv(dev);
157 struct macvtap_queue *q; 216 struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES];
217 int i, j = 0;
158 218
219 /* macvtap_put_queue can free some slots, so go through all slots */
159 spin_lock(&macvtap_lock); 220 spin_lock(&macvtap_lock);
160 q = rcu_dereference(vlan->tap); 221 for (i = 0; i < MAX_MACVTAP_QUEUES && vlan->numvtaps; i++) {
161 if (!q) { 222 q = rcu_dereference(vlan->taps[i]);
162 spin_unlock(&macvtap_lock); 223 if (q) {
163 return; 224 qlist[j++] = q;
225 rcu_assign_pointer(vlan->taps[i], NULL);
226 rcu_assign_pointer(q->vlan, NULL);
227 vlan->numvtaps--;
228 }
164 } 229 }
165 230 BUG_ON(vlan->numvtaps != 0);
166 rcu_assign_pointer(vlan->tap, NULL);
167 rcu_assign_pointer(q->vlan, NULL);
168 spin_unlock(&macvtap_lock); 231 spin_unlock(&macvtap_lock);
169 232
170 synchronize_rcu(); 233 synchronize_rcu();
171 sock_put(&q->sk); 234
235 for (--j; j >= 0; j--)
236 sock_put(&qlist[j]->sk);
172} 237}
173 238
174/* 239/*
diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h
index 35280b30229..8a2fd66a8b5 100644
--- a/include/linux/if_macvlan.h
+++ b/include/linux/if_macvlan.h
@@ -40,6 +40,12 @@ struct macvlan_rx_stats {
40 unsigned long rx_errors; 40 unsigned long rx_errors;
41}; 41};
42 42
43/*
44 * Maximum times a macvtap device can be opened. This can be used to
45 * configure the number of receive queue, e.g. for multiqueue virtio.
46 */
47#define MAX_MACVTAP_QUEUES (NR_CPUS < 16 ? NR_CPUS : 16)
48
43struct macvlan_dev { 49struct macvlan_dev {
44 struct net_device *dev; 50 struct net_device *dev;
45 struct list_head list; 51 struct list_head list;
@@ -50,7 +56,8 @@ struct macvlan_dev {
50 enum macvlan_mode mode; 56 enum macvlan_mode mode;
51 int (*receive)(struct sk_buff *skb); 57 int (*receive)(struct sk_buff *skb);
52 int (*forward)(struct net_device *dev, struct sk_buff *skb); 58 int (*forward)(struct net_device *dev, struct sk_buff *skb);
53 struct macvtap_queue *tap; 59 struct macvtap_queue *taps[MAX_MACVTAP_QUEUES];
60 int numvtaps;
54}; 61};
55 62
56static inline void macvlan_count_rx(const struct macvlan_dev *vlan, 63static inline void macvlan_count_rx(const struct macvlan_dev *vlan,