aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/macvtap.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2013-06-05 19:54:39 -0400
committerDavid S. Miller <davem@davemloft.net>2013-06-08 02:49:09 -0400
commit815f236d622721b54f3963ba59dad98b02cdeabf (patch)
tree1d250c5ea79bb8f5352fb1c0a978352cc9b3a3e1 /drivers/net/macvtap.c
parent376b1aabe1f53aad80615d05ddb8c43670be6a5c (diff)
macvtap: add TUNSETQUEUE ioctl
This patch adds TUNSETQUEUE ioctl to let userspace can temporarily disable or enable a queue of macvtap. This is used to be compatible at API layer of tuntap to simplify the userspace to manage the queues. This is done through introducing a linked list to track all taps while using vlan->taps array to only track active taps. 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.c135
1 files changed, 116 insertions, 19 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 5ccba99b15f4..d2d1d5578b61 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -45,6 +45,8 @@ struct macvtap_queue {
45 struct file *file; 45 struct file *file;
46 unsigned int flags; 46 unsigned int flags;
47 u16 queue_index; 47 u16 queue_index;
48 bool enabled;
49 struct list_head next;
48}; 50};
49 51
50static struct proto macvtap_proto = { 52static struct proto macvtap_proto = {
@@ -85,14 +87,36 @@ static const struct proto_ops macvtap_socket_ops;
85 */ 87 */
86static DEFINE_SPINLOCK(macvtap_lock); 88static DEFINE_SPINLOCK(macvtap_lock);
87 89
88static int macvtap_set_queue(struct net_device *dev, struct file *file, 90static int macvtap_enable_queue(struct net_device *dev, struct file *file,
89 struct macvtap_queue *q) 91 struct macvtap_queue *q)
90{ 92{
91 struct macvlan_dev *vlan = netdev_priv(dev); 93 struct macvlan_dev *vlan = netdev_priv(dev);
94 int err = -EINVAL;
95
96 spin_lock(&macvtap_lock);
97
98 if (q->enabled)
99 goto out;
100
101 err = 0;
102 rcu_assign_pointer(vlan->taps[vlan->numvtaps], q);
103 q->queue_index = vlan->numvtaps;
104 q->enabled = true;
105
106 vlan->numvtaps++;
107out:
108 spin_unlock(&macvtap_lock);
109 return err;
110}
111
112static int macvtap_set_queue(struct net_device *dev, struct file *file,
113 struct macvtap_queue *q)
114{
115 struct macvlan_dev *vlan = netdev_priv(dev);
92 int err = -EBUSY; 116 int err = -EBUSY;
93 117
94 spin_lock(&macvtap_lock); 118 spin_lock(&macvtap_lock);
95 if (vlan->numvtaps == MAX_MACVTAP_QUEUES) 119 if (vlan->numqueues == MAX_MACVTAP_QUEUES)
96 goto out; 120 goto out;
97 121
98 err = 0; 122 err = 0;
@@ -102,15 +126,57 @@ static int macvtap_set_queue(struct net_device *dev, struct file *file,
102 126
103 q->file = file; 127 q->file = file;
104 q->queue_index = vlan->numvtaps; 128 q->queue_index = vlan->numvtaps;
129 q->enabled = true;
105 file->private_data = q; 130 file->private_data = q;
131 list_add_tail(&q->next, &vlan->queue_list);
106 132
107 vlan->numvtaps++; 133 vlan->numvtaps++;
134 vlan->numqueues++;
108 135
109out: 136out:
110 spin_unlock(&macvtap_lock); 137 spin_unlock(&macvtap_lock);
111 return err; 138 return err;
112} 139}
113 140
141static int __macvtap_disable_queue(struct macvtap_queue *q)
142{
143 struct macvlan_dev *vlan;
144 struct macvtap_queue *nq;
145
146 vlan = rcu_dereference_protected(q->vlan,
147 lockdep_is_held(&macvtap_lock));
148
149 if (!q->enabled)
150 return -EINVAL;
151
152 if (vlan) {
153 int index = q->queue_index;
154 BUG_ON(index >= vlan->numvtaps);
155 nq = rcu_dereference_protected(vlan->taps[vlan->numvtaps - 1],
156 lockdep_is_held(&macvtap_lock));
157 nq->queue_index = index;
158
159 rcu_assign_pointer(vlan->taps[index], nq);
160 RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL);
161 q->enabled = false;
162
163 vlan->numvtaps--;
164 }
165
166 return 0;
167}
168
169static int macvtap_disable_queue(struct macvtap_queue *q)
170{
171 int err;
172
173 spin_lock(&macvtap_lock);
174 err = __macvtap_disable_queue(q);
175 spin_unlock(&macvtap_lock);
176
177 return err;
178}
179
114/* 180/*
115 * The file owning the queue got closed, give up both 181 * The file owning the queue got closed, give up both
116 * the reference that the files holds as well as the 182 * the reference that the files holds as well as the
@@ -121,25 +187,19 @@ out:
121 */ 187 */
122static void macvtap_put_queue(struct macvtap_queue *q) 188static void macvtap_put_queue(struct macvtap_queue *q)
123{ 189{
124 struct macvtap_queue *nq;
125 struct macvlan_dev *vlan; 190 struct macvlan_dev *vlan;
126 191
127 spin_lock(&macvtap_lock); 192 spin_lock(&macvtap_lock);
128 vlan = rcu_dereference_protected(q->vlan, 193 vlan = rcu_dereference_protected(q->vlan,
129 lockdep_is_held(&macvtap_lock)); 194 lockdep_is_held(&macvtap_lock));
130 if (vlan) { 195 if (vlan) {
131 int index = q->queue_index; 196 if (q->enabled)
132 BUG_ON(index >= vlan->numvtaps); 197 BUG_ON(__macvtap_disable_queue(q));
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;
138 198
139 RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); 199 vlan->numqueues--;
140 RCU_INIT_POINTER(q->vlan, NULL); 200 RCU_INIT_POINTER(q->vlan, NULL);
141 sock_put(&q->sk); 201 sock_put(&q->sk);
142 --vlan->numvtaps; 202 list_del_init(&q->next);
143 } 203 }
144 204
145 spin_unlock(&macvtap_lock); 205 spin_unlock(&macvtap_lock);
@@ -160,6 +220,11 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev,
160{ 220{
161 struct macvlan_dev *vlan = netdev_priv(dev); 221 struct macvlan_dev *vlan = netdev_priv(dev);
162 struct macvtap_queue *tap = NULL; 222 struct macvtap_queue *tap = NULL;
223 /* Access to taps array is protected by rcu, but access to numvtaps
224 * isn't. Below we use it to lookup a queue, but treat it as a hint
225 * and validate that the result isn't NULL - in case we are
226 * racing against queue removal.
227 */
163 int numvtaps = ACCESS_ONCE(vlan->numvtaps); 228 int numvtaps = ACCESS_ONCE(vlan->numvtaps);
164 __u32 rxq; 229 __u32 rxq;
165 230
@@ -196,18 +261,22 @@ out:
196static void macvtap_del_queues(struct net_device *dev) 261static void macvtap_del_queues(struct net_device *dev)
197{ 262{
198 struct macvlan_dev *vlan = netdev_priv(dev); 263 struct macvlan_dev *vlan = netdev_priv(dev);
199 struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES]; 264 struct macvtap_queue *q, *tmp, *qlist[MAX_MACVTAP_QUEUES];
200 int i, j = 0; 265 int i, j = 0;
201 266
202 spin_lock(&macvtap_lock); 267 spin_lock(&macvtap_lock);
203 for (i = 0; i < vlan->numvtaps; i++) { 268 list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) {
204 q = rcu_dereference_protected(vlan->taps[i], 269 list_del_init(&q->next);
205 lockdep_is_held(&macvtap_lock));
206 BUG_ON(q == NULL);
207 qlist[j++] = q; 270 qlist[j++] = q;
208 RCU_INIT_POINTER(vlan->taps[i], NULL);
209 RCU_INIT_POINTER(q->vlan, NULL); 271 RCU_INIT_POINTER(q->vlan, NULL);
272 if (q->enabled)
273 vlan->numvtaps--;
274 vlan->numqueues--;
210 } 275 }
276 for (i = 0; i < vlan->numvtaps; i++)
277 RCU_INIT_POINTER(vlan->taps[i], NULL);
278 BUG_ON(vlan->numvtaps);
279 BUG_ON(vlan->numqueues);
211 /* guarantee that any future macvtap_set_queue will fail */ 280 /* guarantee that any future macvtap_set_queue will fail */
212 vlan->numvtaps = MAX_MACVTAP_QUEUES; 281 vlan->numvtaps = MAX_MACVTAP_QUEUES;
213 spin_unlock(&macvtap_lock); 282 spin_unlock(&macvtap_lock);
@@ -298,6 +367,9 @@ static int macvtap_newlink(struct net *src_net,
298 struct nlattr *tb[], 367 struct nlattr *tb[],
299 struct nlattr *data[]) 368 struct nlattr *data[])
300{ 369{
370 struct macvlan_dev *vlan = netdev_priv(dev);
371 INIT_LIST_HEAD(&vlan->queue_list);
372
301 /* Don't put anything that may fail after macvlan_common_newlink 373 /* Don't put anything that may fail after macvlan_common_newlink
302 * because we can't undo what it does. 374 * because we can't undo what it does.
303 */ 375 */
@@ -887,6 +959,25 @@ static void macvtap_put_vlan(struct macvlan_dev *vlan)
887 dev_put(vlan->dev); 959 dev_put(vlan->dev);
888} 960}
889 961
962static int macvtap_ioctl_set_queue(struct file *file, unsigned int flags)
963{
964 struct macvtap_queue *q = file->private_data;
965 struct macvlan_dev *vlan;
966 int ret;
967
968 vlan = macvtap_get_vlan(q);
969 if (!vlan)
970 return -EINVAL;
971
972 if (flags & IFF_ATTACH_QUEUE)
973 ret = macvtap_enable_queue(vlan->dev, file, q);
974 else if (flags & IFF_DETACH_QUEUE)
975 ret = macvtap_disable_queue(q);
976
977 macvtap_put_vlan(vlan);
978 return ret;
979}
980
890/* 981/*
891 * provide compatibility with generic tun/tap interface 982 * provide compatibility with generic tun/tap interface
892 */ 983 */
@@ -910,7 +1001,8 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
910 return -EFAULT; 1001 return -EFAULT;
911 1002
912 ret = 0; 1003 ret = 0;
913 if ((u & ~IFF_VNET_HDR) != (IFF_NO_PI | IFF_TAP)) 1004 if ((u & ~(IFF_VNET_HDR | IFF_MULTI_QUEUE)) !=
1005 (IFF_NO_PI | IFF_TAP))
914 ret = -EINVAL; 1006 ret = -EINVAL;
915 else 1007 else
916 q->flags = u; 1008 q->flags = u;
@@ -929,6 +1021,11 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
929 macvtap_put_vlan(vlan); 1021 macvtap_put_vlan(vlan);
930 return ret; 1022 return ret;
931 1023
1024 case TUNSETQUEUE:
1025 if (get_user(u, &ifr->ifr_flags))
1026 return -EFAULT;
1027 return macvtap_ioctl_set_queue(file, u);
1028
932 case TUNGETFEATURES: 1029 case TUNGETFEATURES:
933 if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up)) 1030 if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up))
934 return -EFAULT; 1031 return -EFAULT;