diff options
Diffstat (limited to 'net/openvswitch/datapath.c')
-rw-r--r-- | net/openvswitch/datapath.c | 110 |
1 files changed, 77 insertions, 33 deletions
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index cad39fca75a9..105a0b5adc51 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -116,7 +116,7 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex) | |||
116 | /* Must be called with rcu_read_lock or RTNL lock. */ | 116 | /* Must be called with rcu_read_lock or RTNL lock. */ |
117 | const char *ovs_dp_name(const struct datapath *dp) | 117 | const char *ovs_dp_name(const struct datapath *dp) |
118 | { | 118 | { |
119 | struct vport *vport = rcu_dereference_rtnl(dp->ports[OVSP_LOCAL]); | 119 | struct vport *vport = ovs_vport_rtnl_rcu(dp, OVSP_LOCAL); |
120 | return vport->ops->get_name(vport); | 120 | return vport->ops->get_name(vport); |
121 | } | 121 | } |
122 | 122 | ||
@@ -127,7 +127,7 @@ static int get_dpifindex(struct datapath *dp) | |||
127 | 127 | ||
128 | rcu_read_lock(); | 128 | rcu_read_lock(); |
129 | 129 | ||
130 | local = rcu_dereference(dp->ports[OVSP_LOCAL]); | 130 | local = ovs_vport_rcu(dp, OVSP_LOCAL); |
131 | if (local) | 131 | if (local) |
132 | ifindex = local->ops->get_ifindex(local); | 132 | ifindex = local->ops->get_ifindex(local); |
133 | else | 133 | else |
@@ -145,9 +145,30 @@ static void destroy_dp_rcu(struct rcu_head *rcu) | |||
145 | ovs_flow_tbl_destroy((__force struct flow_table *)dp->table); | 145 | ovs_flow_tbl_destroy((__force struct flow_table *)dp->table); |
146 | free_percpu(dp->stats_percpu); | 146 | free_percpu(dp->stats_percpu); |
147 | release_net(ovs_dp_get_net(dp)); | 147 | release_net(ovs_dp_get_net(dp)); |
148 | kfree(dp->ports); | ||
148 | kfree(dp); | 149 | kfree(dp); |
149 | } | 150 | } |
150 | 151 | ||
152 | static struct hlist_head *vport_hash_bucket(const struct datapath *dp, | ||
153 | u16 port_no) | ||
154 | { | ||
155 | return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)]; | ||
156 | } | ||
157 | |||
158 | struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no) | ||
159 | { | ||
160 | struct vport *vport; | ||
161 | struct hlist_node *n; | ||
162 | struct hlist_head *head; | ||
163 | |||
164 | head = vport_hash_bucket(dp, port_no); | ||
165 | hlist_for_each_entry_rcu(vport, n, head, dp_hash_node) { | ||
166 | if (vport->port_no == port_no) | ||
167 | return vport; | ||
168 | } | ||
169 | return NULL; | ||
170 | } | ||
171 | |||
151 | /* Called with RTNL lock and genl_lock. */ | 172 | /* Called with RTNL lock and genl_lock. */ |
152 | static struct vport *new_vport(const struct vport_parms *parms) | 173 | static struct vport *new_vport(const struct vport_parms *parms) |
153 | { | 174 | { |
@@ -156,9 +177,9 @@ static struct vport *new_vport(const struct vport_parms *parms) | |||
156 | vport = ovs_vport_add(parms); | 177 | vport = ovs_vport_add(parms); |
157 | if (!IS_ERR(vport)) { | 178 | if (!IS_ERR(vport)) { |
158 | struct datapath *dp = parms->dp; | 179 | struct datapath *dp = parms->dp; |
180 | struct hlist_head *head = vport_hash_bucket(dp, vport->port_no); | ||
159 | 181 | ||
160 | rcu_assign_pointer(dp->ports[parms->port_no], vport); | 182 | hlist_add_head_rcu(&vport->dp_hash_node, head); |
161 | list_add(&vport->node, &dp->port_list); | ||
162 | } | 183 | } |
163 | 184 | ||
164 | return vport; | 185 | return vport; |
@@ -170,8 +191,7 @@ void ovs_dp_detach_port(struct vport *p) | |||
170 | ASSERT_RTNL(); | 191 | ASSERT_RTNL(); |
171 | 192 | ||
172 | /* First drop references to device. */ | 193 | /* First drop references to device. */ |
173 | list_del(&p->node); | 194 | hlist_del_rcu(&p->dp_hash_node); |
174 | rcu_assign_pointer(p->dp->ports[p->port_no], NULL); | ||
175 | 195 | ||
176 | /* Then destroy it. */ | 196 | /* Then destroy it. */ |
177 | ovs_vport_del(p); | 197 | ovs_vport_del(p); |
@@ -1248,7 +1268,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1248 | struct datapath *dp; | 1268 | struct datapath *dp; |
1249 | struct vport *vport; | 1269 | struct vport *vport; |
1250 | struct ovs_net *ovs_net; | 1270 | struct ovs_net *ovs_net; |
1251 | int err; | 1271 | int err, i; |
1252 | 1272 | ||
1253 | err = -EINVAL; | 1273 | err = -EINVAL; |
1254 | if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID]) | 1274 | if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID]) |
@@ -1261,7 +1281,6 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1261 | if (dp == NULL) | 1281 | if (dp == NULL) |
1262 | goto err_unlock_rtnl; | 1282 | goto err_unlock_rtnl; |
1263 | 1283 | ||
1264 | INIT_LIST_HEAD(&dp->port_list); | ||
1265 | ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); | 1284 | ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); |
1266 | 1285 | ||
1267 | /* Allocate table. */ | 1286 | /* Allocate table. */ |
@@ -1276,6 +1295,16 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1276 | goto err_destroy_table; | 1295 | goto err_destroy_table; |
1277 | } | 1296 | } |
1278 | 1297 | ||
1298 | dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head), | ||
1299 | GFP_KERNEL); | ||
1300 | if (!dp->ports) { | ||
1301 | err = -ENOMEM; | ||
1302 | goto err_destroy_percpu; | ||
1303 | } | ||
1304 | |||
1305 | for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) | ||
1306 | INIT_HLIST_HEAD(&dp->ports[i]); | ||
1307 | |||
1279 | /* Set up our datapath device. */ | 1308 | /* Set up our datapath device. */ |
1280 | parms.name = nla_data(a[OVS_DP_ATTR_NAME]); | 1309 | parms.name = nla_data(a[OVS_DP_ATTR_NAME]); |
1281 | parms.type = OVS_VPORT_TYPE_INTERNAL; | 1310 | parms.type = OVS_VPORT_TYPE_INTERNAL; |
@@ -1290,7 +1319,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1290 | if (err == -EBUSY) | 1319 | if (err == -EBUSY) |
1291 | err = -EEXIST; | 1320 | err = -EEXIST; |
1292 | 1321 | ||
1293 | goto err_destroy_percpu; | 1322 | goto err_destroy_ports_array; |
1294 | } | 1323 | } |
1295 | 1324 | ||
1296 | reply = ovs_dp_cmd_build_info(dp, info->snd_pid, | 1325 | reply = ovs_dp_cmd_build_info(dp, info->snd_pid, |
@@ -1309,7 +1338,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1309 | return 0; | 1338 | return 0; |
1310 | 1339 | ||
1311 | err_destroy_local_port: | 1340 | err_destroy_local_port: |
1312 | ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL])); | 1341 | ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL)); |
1342 | err_destroy_ports_array: | ||
1343 | kfree(dp->ports); | ||
1313 | err_destroy_percpu: | 1344 | err_destroy_percpu: |
1314 | free_percpu(dp->stats_percpu); | 1345 | free_percpu(dp->stats_percpu); |
1315 | err_destroy_table: | 1346 | err_destroy_table: |
@@ -1326,15 +1357,21 @@ err: | |||
1326 | /* Called with genl_mutex. */ | 1357 | /* Called with genl_mutex. */ |
1327 | static void __dp_destroy(struct datapath *dp) | 1358 | static void __dp_destroy(struct datapath *dp) |
1328 | { | 1359 | { |
1329 | struct vport *vport, *next_vport; | 1360 | int i; |
1330 | 1361 | ||
1331 | rtnl_lock(); | 1362 | rtnl_lock(); |
1332 | list_for_each_entry_safe(vport, next_vport, &dp->port_list, node) | 1363 | |
1333 | if (vport->port_no != OVSP_LOCAL) | 1364 | for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { |
1334 | ovs_dp_detach_port(vport); | 1365 | struct vport *vport; |
1366 | struct hlist_node *node, *n; | ||
1367 | |||
1368 | hlist_for_each_entry_safe(vport, node, n, &dp->ports[i], dp_hash_node) | ||
1369 | if (vport->port_no != OVSP_LOCAL) | ||
1370 | ovs_dp_detach_port(vport); | ||
1371 | } | ||
1335 | 1372 | ||
1336 | list_del(&dp->list_node); | 1373 | list_del(&dp->list_node); |
1337 | ovs_dp_detach_port(rtnl_dereference(dp->ports[OVSP_LOCAL])); | 1374 | ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL)); |
1338 | 1375 | ||
1339 | /* rtnl_unlock() will wait until all the references to devices that | 1376 | /* rtnl_unlock() will wait until all the references to devices that |
1340 | * are pending unregistration have been dropped. We do it here to | 1377 | * are pending unregistration have been dropped. We do it here to |
@@ -1566,7 +1603,7 @@ static struct vport *lookup_vport(struct net *net, | |||
1566 | if (!dp) | 1603 | if (!dp) |
1567 | return ERR_PTR(-ENODEV); | 1604 | return ERR_PTR(-ENODEV); |
1568 | 1605 | ||
1569 | vport = rcu_dereference_rtnl(dp->ports[port_no]); | 1606 | vport = ovs_vport_rtnl_rcu(dp, port_no); |
1570 | if (!vport) | 1607 | if (!vport) |
1571 | return ERR_PTR(-ENOENT); | 1608 | return ERR_PTR(-ENOENT); |
1572 | return vport; | 1609 | return vport; |
@@ -1603,7 +1640,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1603 | if (port_no >= DP_MAX_PORTS) | 1640 | if (port_no >= DP_MAX_PORTS) |
1604 | goto exit_unlock; | 1641 | goto exit_unlock; |
1605 | 1642 | ||
1606 | vport = rtnl_dereference(dp->ports[port_no]); | 1643 | vport = ovs_vport_rtnl_rcu(dp, port_no); |
1607 | err = -EBUSY; | 1644 | err = -EBUSY; |
1608 | if (vport) | 1645 | if (vport) |
1609 | goto exit_unlock; | 1646 | goto exit_unlock; |
@@ -1613,7 +1650,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
1613 | err = -EFBIG; | 1650 | err = -EFBIG; |
1614 | goto exit_unlock; | 1651 | goto exit_unlock; |
1615 | } | 1652 | } |
1616 | vport = rtnl_dereference(dp->ports[port_no]); | 1653 | vport = ovs_vport_rtnl(dp, port_no); |
1617 | if (!vport) | 1654 | if (!vport) |
1618 | break; | 1655 | break; |
1619 | } | 1656 | } |
@@ -1755,32 +1792,39 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
1755 | { | 1792 | { |
1756 | struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); | 1793 | struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); |
1757 | struct datapath *dp; | 1794 | struct datapath *dp; |
1758 | u32 port_no; | 1795 | int bucket = cb->args[0], skip = cb->args[1]; |
1759 | int retval; | 1796 | int i, j = 0; |
1760 | 1797 | ||
1761 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); | 1798 | dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); |
1762 | if (!dp) | 1799 | if (!dp) |
1763 | return -ENODEV; | 1800 | return -ENODEV; |
1764 | 1801 | ||
1765 | rcu_read_lock(); | 1802 | rcu_read_lock(); |
1766 | for (port_no = cb->args[0]; port_no < DP_MAX_PORTS; port_no++) { | 1803 | for (i = bucket; i < DP_VPORT_HASH_BUCKETS; i++) { |
1767 | struct vport *vport; | 1804 | struct vport *vport; |
1768 | 1805 | struct hlist_node *n; | |
1769 | vport = rcu_dereference(dp->ports[port_no]); | 1806 | |
1770 | if (!vport) | 1807 | j = 0; |
1771 | continue; | 1808 | hlist_for_each_entry_rcu(vport, n, &dp->ports[i], dp_hash_node) { |
1772 | 1809 | if (j >= skip && | |
1773 | if (ovs_vport_cmd_fill_info(vport, skb, NETLINK_CB(cb->skb).pid, | 1810 | ovs_vport_cmd_fill_info(vport, skb, |
1774 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | 1811 | NETLINK_CB(cb->skb).pid, |
1775 | OVS_VPORT_CMD_NEW) < 0) | 1812 | cb->nlh->nlmsg_seq, |
1776 | break; | 1813 | NLM_F_MULTI, |
1814 | OVS_VPORT_CMD_NEW) < 0) | ||
1815 | goto out; | ||
1816 | |||
1817 | j++; | ||
1818 | } | ||
1819 | skip = 0; | ||
1777 | } | 1820 | } |
1821 | out: | ||
1778 | rcu_read_unlock(); | 1822 | rcu_read_unlock(); |
1779 | 1823 | ||
1780 | cb->args[0] = port_no; | 1824 | cb->args[0] = i; |
1781 | retval = skb->len; | 1825 | cb->args[1] = j; |
1782 | 1826 | ||
1783 | return retval; | 1827 | return skb->len; |
1784 | } | 1828 | } |
1785 | 1829 | ||
1786 | static struct genl_ops dp_vport_genl_ops[] = { | 1830 | static struct genl_ops dp_vport_genl_ops[] = { |