diff options
author | Jason Wang <jasowang@redhat.com> | 2013-06-05 19:54:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-06-08 02:49:09 -0400 |
commit | 815f236d622721b54f3963ba59dad98b02cdeabf (patch) | |
tree | 1d250c5ea79bb8f5352fb1c0a978352cc9b3a3e1 /drivers/net/macvtap.c | |
parent | 376b1aabe1f53aad80615d05ddb8c43670be6a5c (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.c | 135 |
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 | ||
50 | static struct proto macvtap_proto = { | 52 | static struct proto macvtap_proto = { |
@@ -85,14 +87,36 @@ static const struct proto_ops macvtap_socket_ops; | |||
85 | */ | 87 | */ |
86 | static DEFINE_SPINLOCK(macvtap_lock); | 88 | static DEFINE_SPINLOCK(macvtap_lock); |
87 | 89 | ||
88 | static int macvtap_set_queue(struct net_device *dev, struct file *file, | 90 | static 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++; | ||
107 | out: | ||
108 | spin_unlock(&macvtap_lock); | ||
109 | return err; | ||
110 | } | ||
111 | |||
112 | static 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 | ||
109 | out: | 136 | out: |
110 | spin_unlock(&macvtap_lock); | 137 | spin_unlock(&macvtap_lock); |
111 | return err; | 138 | return err; |
112 | } | 139 | } |
113 | 140 | ||
141 | static 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 | |||
169 | static 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 | */ |
122 | static void macvtap_put_queue(struct macvtap_queue *q) | 188 | static 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: | |||
196 | static void macvtap_del_queues(struct net_device *dev) | 261 | static 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 | ||
962 | static 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; |