diff options
author | Jason Wang <jasowang@redhat.com> | 2013-06-05 19:54:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-06-08 02:49:09 -0400 |
commit | 376b1aabe1f53aad80615d05ddb8c43670be6a5c (patch) | |
tree | 95c266af703da4637da900c972c841f68948b0de /drivers/net/macvtap.c | |
parent | f0afce01aa639e5164cf73f063b81a8b95619c3a (diff) |
macvtap: eliminate linear search
Linear search were used in both get_slot() and macvtap_get_queue(), this is
because:
- macvtap didn't reshuffle the array of taps when create or destroy a queue, so
when adding a new queue, macvtap must do linear search to find a location for
the new queue. This will also complicate the TUNSETQUEUE implementation for
multiqueue API.
- the queue itself didn't track the queue index, so the we must do a linear
search in the array to find the location of a existed queue.
The solution is straightforward: reshuffle the array and introduce a queue_index
to macvtap_queue.
Signed-off-by: Jason Wang <jasowang@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/macvtap.c')
-rw-r--r-- | drivers/net/macvtap.c | 64 |
1 files changed, 20 insertions, 44 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index d18130b3fe0d..5ccba99b15f4 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c | |||
@@ -44,6 +44,7 @@ struct macvtap_queue { | |||
44 | struct macvlan_dev __rcu *vlan; | 44 | struct macvlan_dev __rcu *vlan; |
45 | struct file *file; | 45 | struct file *file; |
46 | unsigned int flags; | 46 | unsigned int flags; |
47 | u16 queue_index; | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | static struct proto macvtap_proto = { | 50 | static struct proto macvtap_proto = { |
@@ -84,30 +85,10 @@ static const struct proto_ops macvtap_socket_ops; | |||
84 | */ | 85 | */ |
85 | static DEFINE_SPINLOCK(macvtap_lock); | 86 | static DEFINE_SPINLOCK(macvtap_lock); |
86 | 87 | ||
87 | /* | ||
88 | * get_slot: return a [unused/occupied] slot in vlan->taps[]: | ||
89 | * - if 'q' is NULL, return the first empty slot; | ||
90 | * - otherwise, return the slot this pointer occupies. | ||
91 | */ | ||
92 | static int get_slot(struct macvlan_dev *vlan, struct macvtap_queue *q) | ||
93 | { | ||
94 | int i; | ||
95 | |||
96 | for (i = 0; i < MAX_MACVTAP_QUEUES; i++) { | ||
97 | if (rcu_dereference_protected(vlan->taps[i], | ||
98 | lockdep_is_held(&macvtap_lock)) == q) | ||
99 | return i; | ||
100 | } | ||
101 | |||
102 | /* Should never happen */ | ||
103 | BUG_ON(1); | ||
104 | } | ||
105 | |||
106 | static int macvtap_set_queue(struct net_device *dev, struct file *file, | 88 | static int macvtap_set_queue(struct net_device *dev, struct file *file, |
107 | struct macvtap_queue *q) | 89 | struct macvtap_queue *q) |
108 | { | 90 | { |
109 | struct macvlan_dev *vlan = netdev_priv(dev); | 91 | struct macvlan_dev *vlan = netdev_priv(dev); |
110 | int index; | ||
111 | int err = -EBUSY; | 92 | int err = -EBUSY; |
112 | 93 | ||
113 | spin_lock(&macvtap_lock); | 94 | spin_lock(&macvtap_lock); |
@@ -115,12 +96,12 @@ static int macvtap_set_queue(struct net_device *dev, struct file *file, | |||
115 | goto out; | 96 | goto out; |
116 | 97 | ||
117 | err = 0; | 98 | err = 0; |
118 | index = get_slot(vlan, NULL); | ||
119 | rcu_assign_pointer(q->vlan, vlan); | 99 | rcu_assign_pointer(q->vlan, vlan); |
120 | rcu_assign_pointer(vlan->taps[index], q); | 100 | rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); |
121 | sock_hold(&q->sk); | 101 | sock_hold(&q->sk); |
122 | 102 | ||
123 | q->file = file; | 103 | q->file = file; |
104 | q->queue_index = vlan->numvtaps; | ||
124 | file->private_data = q; | 105 | file->private_data = q; |
125 | 106 | ||
126 | vlan->numvtaps++; | 107 | vlan->numvtaps++; |
@@ -140,15 +121,22 @@ out: | |||
140 | */ | 121 | */ |
141 | static void macvtap_put_queue(struct macvtap_queue *q) | 122 | static void macvtap_put_queue(struct macvtap_queue *q) |
142 | { | 123 | { |
124 | struct macvtap_queue *nq; | ||
143 | struct macvlan_dev *vlan; | 125 | struct macvlan_dev *vlan; |
144 | 126 | ||
145 | spin_lock(&macvtap_lock); | 127 | spin_lock(&macvtap_lock); |
146 | vlan = rcu_dereference_protected(q->vlan, | 128 | vlan = rcu_dereference_protected(q->vlan, |
147 | lockdep_is_held(&macvtap_lock)); | 129 | lockdep_is_held(&macvtap_lock)); |
148 | if (vlan) { | 130 | if (vlan) { |
149 | int index = get_slot(vlan, q); | 131 | int index = q->queue_index; |
132 | BUG_ON(index >= vlan->numvtaps); | ||
133 | |||
134 | nq = rcu_dereference_protected(vlan->taps[vlan->numvtaps - 1], | ||
135 | lockdep_is_held(&macvtap_lock)); | ||
136 | rcu_assign_pointer(vlan->taps[index], nq); | ||
137 | nq->queue_index = index; | ||
150 | 138 | ||
151 | RCU_INIT_POINTER(vlan->taps[index], NULL); | 139 | RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); |
152 | RCU_INIT_POINTER(q->vlan, NULL); | 140 | RCU_INIT_POINTER(q->vlan, NULL); |
153 | sock_put(&q->sk); | 141 | sock_put(&q->sk); |
154 | --vlan->numvtaps; | 142 | --vlan->numvtaps; |
@@ -182,8 +170,7 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, | |||
182 | rxq = skb_get_rxhash(skb); | 170 | rxq = skb_get_rxhash(skb); |
183 | if (rxq) { | 171 | if (rxq) { |
184 | tap = rcu_dereference(vlan->taps[rxq % numvtaps]); | 172 | tap = rcu_dereference(vlan->taps[rxq % numvtaps]); |
185 | if (tap) | 173 | goto out; |
186 | goto out; | ||
187 | } | 174 | } |
188 | 175 | ||
189 | if (likely(skb_rx_queue_recorded(skb))) { | 176 | if (likely(skb_rx_queue_recorded(skb))) { |
@@ -193,17 +180,10 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, | |||
193 | rxq -= numvtaps; | 180 | rxq -= numvtaps; |
194 | 181 | ||
195 | tap = rcu_dereference(vlan->taps[rxq]); | 182 | tap = rcu_dereference(vlan->taps[rxq]); |
196 | if (tap) | 183 | goto out; |
197 | goto out; | ||
198 | } | ||
199 | |||
200 | /* Everything failed - find first available queue */ | ||
201 | for (rxq = 0; rxq < MAX_MACVTAP_QUEUES; rxq++) { | ||
202 | tap = rcu_dereference(vlan->taps[rxq]); | ||
203 | if (tap) | ||
204 | break; | ||
205 | } | 184 | } |
206 | 185 | ||
186 | tap = rcu_dereference(vlan->taps[0]); | ||
207 | out: | 187 | out: |
208 | return tap; | 188 | return tap; |
209 | } | 189 | } |
@@ -219,19 +199,15 @@ static void macvtap_del_queues(struct net_device *dev) | |||
219 | struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES]; | 199 | struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES]; |
220 | int i, j = 0; | 200 | int i, j = 0; |
221 | 201 | ||
222 | /* macvtap_put_queue can free some slots, so go through all slots */ | ||
223 | spin_lock(&macvtap_lock); | 202 | spin_lock(&macvtap_lock); |
224 | for (i = 0; i < MAX_MACVTAP_QUEUES && vlan->numvtaps; i++) { | 203 | for (i = 0; i < vlan->numvtaps; i++) { |
225 | q = rcu_dereference_protected(vlan->taps[i], | 204 | q = rcu_dereference_protected(vlan->taps[i], |
226 | lockdep_is_held(&macvtap_lock)); | 205 | lockdep_is_held(&macvtap_lock)); |
227 | if (q) { | 206 | BUG_ON(q == NULL); |
228 | qlist[j++] = q; | 207 | qlist[j++] = q; |
229 | RCU_INIT_POINTER(vlan->taps[i], NULL); | 208 | RCU_INIT_POINTER(vlan->taps[i], NULL); |
230 | RCU_INIT_POINTER(q->vlan, NULL); | 209 | RCU_INIT_POINTER(q->vlan, NULL); |
231 | vlan->numvtaps--; | ||
232 | } | ||
233 | } | 210 | } |
234 | BUG_ON(vlan->numvtaps != 0); | ||
235 | /* guarantee that any future macvtap_set_queue will fail */ | 211 | /* guarantee that any future macvtap_set_queue will fail */ |
236 | vlan->numvtaps = MAX_MACVTAP_QUEUES; | 212 | vlan->numvtaps = MAX_MACVTAP_QUEUES; |
237 | spin_unlock(&macvtap_lock); | 213 | spin_unlock(&macvtap_lock); |