aboutsummaryrefslogtreecommitdiffstats
path: root/net/switchdev/switchdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r--net/switchdev/switchdev.c278
1 files changed, 5 insertions, 273 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index a5fc9dd24aa9..02beb35f577f 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -21,7 +21,6 @@
21#include <linux/workqueue.h> 21#include <linux/workqueue.h>
22#include <linux/if_vlan.h> 22#include <linux/if_vlan.h>
23#include <linux/rtnetlink.h> 23#include <linux/rtnetlink.h>
24#include <net/ip_fib.h>
25#include <net/switchdev.h> 24#include <net/switchdev.h>
26 25
27/** 26/**
@@ -344,8 +343,6 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
344 switch (obj->id) { 343 switch (obj->id) {
345 case SWITCHDEV_OBJ_ID_PORT_VLAN: 344 case SWITCHDEV_OBJ_ID_PORT_VLAN:
346 return sizeof(struct switchdev_obj_port_vlan); 345 return sizeof(struct switchdev_obj_port_vlan);
347 case SWITCHDEV_OBJ_ID_IPV4_FIB:
348 return sizeof(struct switchdev_obj_ipv4_fib);
349 case SWITCHDEV_OBJ_ID_PORT_FDB: 346 case SWITCHDEV_OBJ_ID_PORT_FDB:
350 return sizeof(struct switchdev_obj_port_fdb); 347 return sizeof(struct switchdev_obj_port_fdb);
351 case SWITCHDEV_OBJ_ID_PORT_MDB: 348 case SWITCHDEV_OBJ_ID_PORT_MDB:
@@ -1042,7 +1039,7 @@ static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj)
1042 struct nlmsghdr *nlh; 1039 struct nlmsghdr *nlh;
1043 struct ndmsg *ndm; 1040 struct ndmsg *ndm;
1044 1041
1045 if (dump->idx < dump->cb->args[0]) 1042 if (dump->idx < dump->cb->args[2])
1046 goto skip; 1043 goto skip;
1047 1044
1048 nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, 1045 nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
@@ -1089,7 +1086,7 @@ nla_put_failure:
1089 */ 1086 */
1090int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, 1087int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
1091 struct net_device *dev, 1088 struct net_device *dev,
1092 struct net_device *filter_dev, int idx) 1089 struct net_device *filter_dev, int *idx)
1093{ 1090{
1094 struct switchdev_fdb_dump dump = { 1091 struct switchdev_fdb_dump dump = {
1095 .fdb.obj.orig_dev = dev, 1092 .fdb.obj.orig_dev = dev,
@@ -1097,207 +1094,27 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
1097 .dev = dev, 1094 .dev = dev,
1098 .skb = skb, 1095 .skb = skb,
1099 .cb = cb, 1096 .cb = cb,
1100 .idx = idx, 1097 .idx = *idx,
1101 }; 1098 };
1102 int err; 1099 int err;
1103 1100
1104 err = switchdev_port_obj_dump(dev, &dump.fdb.obj, 1101 err = switchdev_port_obj_dump(dev, &dump.fdb.obj,
1105 switchdev_port_fdb_dump_cb); 1102 switchdev_port_fdb_dump_cb);
1106 cb->args[1] = err; 1103 *idx = dump.idx;
1107 return dump.idx; 1104 return err;
1108} 1105}
1109EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump); 1106EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump);
1110 1107
1111static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
1112{
1113 const struct switchdev_ops *ops = dev->switchdev_ops;
1114 struct net_device *lower_dev;
1115 struct net_device *port_dev;
1116 struct list_head *iter;
1117
1118 /* Recusively search down until we find a sw port dev.
1119 * (A sw port dev supports switchdev_port_attr_get).
1120 */
1121
1122 if (ops && ops->switchdev_port_attr_get)
1123 return dev;
1124
1125 netdev_for_each_lower_dev(dev, lower_dev, iter) {
1126 port_dev = switchdev_get_lowest_dev(lower_dev);
1127 if (port_dev)
1128 return port_dev;
1129 }
1130
1131 return NULL;
1132}
1133
1134static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
1135{
1136 struct switchdev_attr attr = {
1137 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
1138 };
1139 struct switchdev_attr prev_attr;
1140 struct net_device *dev = NULL;
1141 int nhsel;
1142
1143 ASSERT_RTNL();
1144
1145 /* For this route, all nexthop devs must be on the same switch. */
1146
1147 for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
1148 const struct fib_nh *nh = &fi->fib_nh[nhsel];
1149
1150 if (!nh->nh_dev)
1151 return NULL;
1152
1153 dev = switchdev_get_lowest_dev(nh->nh_dev);
1154 if (!dev)
1155 return NULL;
1156
1157 attr.orig_dev = dev;
1158 if (switchdev_port_attr_get(dev, &attr))
1159 return NULL;
1160
1161 if (nhsel > 0 &&
1162 !netdev_phys_item_id_same(&prev_attr.u.ppid, &attr.u.ppid))
1163 return NULL;
1164
1165 prev_attr = attr;
1166 }
1167
1168 return dev;
1169}
1170
1171/**
1172 * switchdev_fib_ipv4_add - Add/modify switch IPv4 route entry
1173 *
1174 * @dst: route's IPv4 destination address
1175 * @dst_len: destination address length (prefix length)
1176 * @fi: route FIB info structure
1177 * @tos: route TOS
1178 * @type: route type
1179 * @nlflags: netlink flags passed in (NLM_F_*)
1180 * @tb_id: route table ID
1181 *
1182 * Add/modify switch IPv4 route entry.
1183 */
1184int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
1185 u8 tos, u8 type, u32 nlflags, u32 tb_id)
1186{
1187 struct switchdev_obj_ipv4_fib ipv4_fib = {
1188 .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB,
1189 .dst = dst,
1190 .dst_len = dst_len,
1191 .fi = fi,
1192 .tos = tos,
1193 .type = type,
1194 .nlflags = nlflags,
1195 .tb_id = tb_id,
1196 };
1197 struct net_device *dev;
1198 int err = 0;
1199
1200 /* Don't offload route if using custom ip rules or if
1201 * IPv4 FIB offloading has been disabled completely.
1202 */
1203
1204#ifdef CONFIG_IP_MULTIPLE_TABLES
1205 if (fi->fib_net->ipv4.fib_has_custom_rules)
1206 return 0;
1207#endif
1208
1209 if (fi->fib_net->ipv4.fib_offload_disabled)
1210 return 0;
1211
1212 dev = switchdev_get_dev_by_nhs(fi);
1213 if (!dev)
1214 return 0;
1215
1216 ipv4_fib.obj.orig_dev = dev;
1217 err = switchdev_port_obj_add(dev, &ipv4_fib.obj);
1218 if (!err)
1219 fi->fib_flags |= RTNH_F_OFFLOAD;
1220
1221 return err == -EOPNOTSUPP ? 0 : err;
1222}
1223EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
1224
1225/**
1226 * switchdev_fib_ipv4_del - Delete IPv4 route entry from switch
1227 *
1228 * @dst: route's IPv4 destination address
1229 * @dst_len: destination address length (prefix length)
1230 * @fi: route FIB info structure
1231 * @tos: route TOS
1232 * @type: route type
1233 * @tb_id: route table ID
1234 *
1235 * Delete IPv4 route entry from switch device.
1236 */
1237int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
1238 u8 tos, u8 type, u32 tb_id)
1239{
1240 struct switchdev_obj_ipv4_fib ipv4_fib = {
1241 .obj.id = SWITCHDEV_OBJ_ID_IPV4_FIB,
1242 .dst = dst,
1243 .dst_len = dst_len,
1244 .fi = fi,
1245 .tos = tos,
1246 .type = type,
1247 .nlflags = 0,
1248 .tb_id = tb_id,
1249 };
1250 struct net_device *dev;
1251 int err = 0;
1252
1253 if (!(fi->fib_flags & RTNH_F_OFFLOAD))
1254 return 0;
1255
1256 dev = switchdev_get_dev_by_nhs(fi);
1257 if (!dev)
1258 return 0;
1259
1260 ipv4_fib.obj.orig_dev = dev;
1261 err = switchdev_port_obj_del(dev, &ipv4_fib.obj);
1262 if (!err)
1263 fi->fib_flags &= ~RTNH_F_OFFLOAD;
1264
1265 return err == -EOPNOTSUPP ? 0 : err;
1266}
1267EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del);
1268
1269/**
1270 * switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation
1271 *
1272 * @fi: route FIB info structure
1273 */
1274void switchdev_fib_ipv4_abort(struct fib_info *fi)
1275{
1276 /* There was a problem installing this route to the offload
1277 * device. For now, until we come up with more refined
1278 * policy handling, abruptly end IPv4 fib offloading for
1279 * for entire net by flushing offload device(s) of all
1280 * IPv4 routes, and mark IPv4 fib offloading broken from
1281 * this point forward.
1282 */
1283
1284 fib_flush_external(fi->fib_net);
1285 fi->fib_net->ipv4.fib_offload_disabled = true;
1286}
1287EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort);
1288
1289bool switchdev_port_same_parent_id(struct net_device *a, 1108bool switchdev_port_same_parent_id(struct net_device *a,
1290 struct net_device *b) 1109 struct net_device *b)
1291{ 1110{
1292 struct switchdev_attr a_attr = { 1111 struct switchdev_attr a_attr = {
1293 .orig_dev = a, 1112 .orig_dev = a,
1294 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, 1113 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
1295 .flags = SWITCHDEV_F_NO_RECURSE,
1296 }; 1114 };
1297 struct switchdev_attr b_attr = { 1115 struct switchdev_attr b_attr = {
1298 .orig_dev = b, 1116 .orig_dev = b,
1299 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, 1117 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
1300 .flags = SWITCHDEV_F_NO_RECURSE,
1301 }; 1118 };
1302 1119
1303 if (switchdev_port_attr_get(a, &a_attr) || 1120 if (switchdev_port_attr_get(a, &a_attr) ||
@@ -1306,89 +1123,4 @@ bool switchdev_port_same_parent_id(struct net_device *a,
1306 1123
1307 return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid); 1124 return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
1308} 1125}
1309
1310static u32 switchdev_port_fwd_mark_get(struct net_device *dev,
1311 struct net_device *group_dev)
1312{
1313 struct net_device *lower_dev;
1314 struct list_head *iter;
1315
1316 netdev_for_each_lower_dev(group_dev, lower_dev, iter) {
1317 if (lower_dev == dev)
1318 continue;
1319 if (switchdev_port_same_parent_id(dev, lower_dev))
1320 return lower_dev->offload_fwd_mark;
1321 return switchdev_port_fwd_mark_get(dev, lower_dev);
1322 }
1323
1324 return dev->ifindex;
1325}
1326EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); 1126EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
1327
1328static void switchdev_port_fwd_mark_reset(struct net_device *group_dev,
1329 u32 old_mark, u32 *reset_mark)
1330{
1331 struct net_device *lower_dev;
1332 struct list_head *iter;
1333
1334 netdev_for_each_lower_dev(group_dev, lower_dev, iter) {
1335 if (lower_dev->offload_fwd_mark == old_mark) {
1336 if (!*reset_mark)
1337 *reset_mark = lower_dev->ifindex;
1338 lower_dev->offload_fwd_mark = *reset_mark;
1339 }
1340 switchdev_port_fwd_mark_reset(lower_dev, old_mark, reset_mark);
1341 }
1342}
1343
1344/**
1345 * switchdev_port_fwd_mark_set - Set port offload forwarding mark
1346 *
1347 * @dev: port device
1348 * @group_dev: containing device
1349 * @joining: true if dev is joining group; false if leaving group
1350 *
1351 * An ungrouped port's offload mark is just its ifindex. A grouped
1352 * port's (member of a bridge, for example) offload mark is the ifindex
1353 * of one of the ports in the group with the same parent (switch) ID.
1354 * Ports on the same device in the same group will have the same mark.
1355 *
1356 * Example:
1357 *
1358 * br0 ifindex=9
1359 * sw1p1 ifindex=2 mark=2
1360 * sw1p2 ifindex=3 mark=2
1361 * sw2p1 ifindex=4 mark=5
1362 * sw2p2 ifindex=5 mark=5
1363 *
1364 * If sw2p2 leaves the bridge, we'll have:
1365 *
1366 * br0 ifindex=9
1367 * sw1p1 ifindex=2 mark=2
1368 * sw1p2 ifindex=3 mark=2
1369 * sw2p1 ifindex=4 mark=4
1370 * sw2p2 ifindex=5 mark=5
1371 */
1372void switchdev_port_fwd_mark_set(struct net_device *dev,
1373 struct net_device *group_dev,
1374 bool joining)
1375{
1376 u32 mark = dev->ifindex;
1377 u32 reset_mark = 0;
1378
1379 if (group_dev) {
1380 ASSERT_RTNL();
1381 if (joining)
1382 mark = switchdev_port_fwd_mark_get(dev, group_dev);
1383 else if (dev->offload_fwd_mark == mark)
1384 /* Ohoh, this port was the mark reference port,
1385 * but it's leaving the group, so reset the
1386 * mark for the remaining ports in the group.
1387 */
1388 switchdev_port_fwd_mark_reset(group_dev, mark,
1389 &reset_mark);
1390 }
1391
1392 dev->offload_fwd_mark = mark;
1393}
1394EXPORT_SYMBOL_GPL(switchdev_port_fwd_mark_set);