diff options
Diffstat (limited to 'drivers/net/team')
-rw-r--r-- | drivers/net/team/team.c | 177 |
1 files changed, 93 insertions, 84 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 70d5d6bdf583..738f7445ea87 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -1965,30 +1965,6 @@ static void team_nl_team_put(struct team *team) | |||
1965 | dev_put(team->dev); | 1965 | dev_put(team->dev); |
1966 | } | 1966 | } |
1967 | 1967 | ||
1968 | static int team_nl_send_generic(struct genl_info *info, struct team *team, | ||
1969 | int (*fill_func)(struct sk_buff *skb, | ||
1970 | struct genl_info *info, | ||
1971 | int flags, struct team *team)) | ||
1972 | { | ||
1973 | struct sk_buff *skb; | ||
1974 | int err; | ||
1975 | |||
1976 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
1977 | if (!skb) | ||
1978 | return -ENOMEM; | ||
1979 | |||
1980 | err = fill_func(skb, info, NLM_F_ACK, team); | ||
1981 | if (err < 0) | ||
1982 | goto err_fill; | ||
1983 | |||
1984 | err = genlmsg_unicast(genl_info_net(info), skb, info->snd_portid); | ||
1985 | return err; | ||
1986 | |||
1987 | err_fill: | ||
1988 | nlmsg_free(skb); | ||
1989 | return err; | ||
1990 | } | ||
1991 | |||
1992 | typedef int team_nl_send_func_t(struct sk_buff *skb, | 1968 | typedef int team_nl_send_func_t(struct sk_buff *skb, |
1993 | struct team *team, u32 portid); | 1969 | struct team *team, u32 portid); |
1994 | 1970 | ||
@@ -2333,16 +2309,57 @@ team_put: | |||
2333 | return err; | 2309 | return err; |
2334 | } | 2310 | } |
2335 | 2311 | ||
2336 | static int team_nl_fill_port_list_get(struct sk_buff *skb, | 2312 | static int team_nl_fill_one_port_get(struct sk_buff *skb, |
2337 | u32 portid, u32 seq, int flags, | 2313 | struct team_port *port) |
2338 | struct team *team, | 2314 | { |
2339 | bool fillall) | 2315 | struct nlattr *port_item; |
2316 | |||
2317 | port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); | ||
2318 | if (!port_item) | ||
2319 | goto nest_cancel; | ||
2320 | if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) | ||
2321 | goto nest_cancel; | ||
2322 | if (port->changed) { | ||
2323 | if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED)) | ||
2324 | goto nest_cancel; | ||
2325 | port->changed = false; | ||
2326 | } | ||
2327 | if ((port->removed && | ||
2328 | nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) || | ||
2329 | (port->state.linkup && | ||
2330 | nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) || | ||
2331 | nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) || | ||
2332 | nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex)) | ||
2333 | goto nest_cancel; | ||
2334 | nla_nest_end(skb, port_item); | ||
2335 | return 0; | ||
2336 | |||
2337 | nest_cancel: | ||
2338 | nla_nest_cancel(skb, port_item); | ||
2339 | return -EMSGSIZE; | ||
2340 | } | ||
2341 | |||
2342 | static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq, | ||
2343 | int flags, team_nl_send_func_t *send_func, | ||
2344 | struct team_port *one_port) | ||
2340 | { | 2345 | { |
2341 | struct nlattr *port_list; | 2346 | struct nlattr *port_list; |
2347 | struct nlmsghdr *nlh; | ||
2342 | void *hdr; | 2348 | void *hdr; |
2343 | struct team_port *port; | 2349 | struct team_port *port; |
2350 | int err; | ||
2351 | struct sk_buff *skb = NULL; | ||
2352 | bool incomplete; | ||
2353 | int i; | ||
2344 | 2354 | ||
2345 | hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags, | 2355 | port = list_first_entry(&team->port_list, struct team_port, list); |
2356 | |||
2357 | start_again: | ||
2358 | err = __send_and_alloc_skb(&skb, team, portid, send_func); | ||
2359 | if (err) | ||
2360 | return err; | ||
2361 | |||
2362 | hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI, | ||
2346 | TEAM_CMD_PORT_LIST_GET); | 2363 | TEAM_CMD_PORT_LIST_GET); |
2347 | if (!hdr) | 2364 | if (!hdr) |
2348 | return -EMSGSIZE; | 2365 | return -EMSGSIZE; |
@@ -2353,47 +2370,54 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, | |||
2353 | if (!port_list) | 2370 | if (!port_list) |
2354 | goto nla_put_failure; | 2371 | goto nla_put_failure; |
2355 | 2372 | ||
2356 | list_for_each_entry(port, &team->port_list, list) { | 2373 | i = 0; |
2357 | struct nlattr *port_item; | 2374 | incomplete = false; |
2358 | 2375 | ||
2359 | /* Include only changed ports if fill all mode is not on */ | 2376 | /* If one port is selected, called wants to send port list containing |
2360 | if (!fillall && !port->changed) | 2377 | * only this port. Otherwise go through all listed ports and send all |
2361 | continue; | 2378 | */ |
2362 | port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT); | 2379 | if (one_port) { |
2363 | if (!port_item) | 2380 | err = team_nl_fill_one_port_get(skb, one_port); |
2364 | goto nla_put_failure; | 2381 | if (err) |
2365 | if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex)) | 2382 | goto errout; |
2366 | goto nla_put_failure; | 2383 | } else { |
2367 | if (port->changed) { | 2384 | list_for_each_entry(port, &team->port_list, list) { |
2368 | if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED)) | 2385 | err = team_nl_fill_one_port_get(skb, port); |
2369 | goto nla_put_failure; | 2386 | if (err) { |
2370 | port->changed = false; | 2387 | if (err == -EMSGSIZE) { |
2388 | if (!i) | ||
2389 | goto errout; | ||
2390 | incomplete = true; | ||
2391 | break; | ||
2392 | } | ||
2393 | goto errout; | ||
2394 | } | ||
2395 | i++; | ||
2371 | } | 2396 | } |
2372 | if ((port->removed && | ||
2373 | nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) || | ||
2374 | (port->state.linkup && | ||
2375 | nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) || | ||
2376 | nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) || | ||
2377 | nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex)) | ||
2378 | goto nla_put_failure; | ||
2379 | nla_nest_end(skb, port_item); | ||
2380 | } | 2397 | } |
2381 | 2398 | ||
2382 | nla_nest_end(skb, port_list); | 2399 | nla_nest_end(skb, port_list); |
2383 | return genlmsg_end(skb, hdr); | 2400 | genlmsg_end(skb, hdr); |
2401 | if (incomplete) | ||
2402 | goto start_again; | ||
2403 | |||
2404 | send_done: | ||
2405 | nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI); | ||
2406 | if (!nlh) { | ||
2407 | err = __send_and_alloc_skb(&skb, team, portid, send_func); | ||
2408 | if (err) | ||
2409 | goto errout; | ||
2410 | goto send_done; | ||
2411 | } | ||
2412 | |||
2413 | return send_func(skb, team, portid); | ||
2384 | 2414 | ||
2385 | nla_put_failure: | 2415 | nla_put_failure: |
2416 | err = -EMSGSIZE; | ||
2417 | errout: | ||
2386 | genlmsg_cancel(skb, hdr); | 2418 | genlmsg_cancel(skb, hdr); |
2387 | return -EMSGSIZE; | 2419 | nlmsg_free(skb); |
2388 | } | 2420 | return err; |
2389 | |||
2390 | static int team_nl_fill_port_list_get_all(struct sk_buff *skb, | ||
2391 | struct genl_info *info, int flags, | ||
2392 | struct team *team) | ||
2393 | { | ||
2394 | return team_nl_fill_port_list_get(skb, info->snd_portid, | ||
2395 | info->snd_seq, NLM_F_ACK, | ||
2396 | team, true); | ||
2397 | } | 2421 | } |
2398 | 2422 | ||
2399 | static int team_nl_cmd_port_list_get(struct sk_buff *skb, | 2423 | static int team_nl_cmd_port_list_get(struct sk_buff *skb, |
@@ -2406,7 +2430,8 @@ static int team_nl_cmd_port_list_get(struct sk_buff *skb, | |||
2406 | if (!team) | 2430 | if (!team) |
2407 | return -EINVAL; | 2431 | return -EINVAL; |
2408 | 2432 | ||
2409 | err = team_nl_send_generic(info, team, team_nl_fill_port_list_get_all); | 2433 | err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq, |
2434 | NLM_F_ACK, team_nl_send_unicast, NULL); | ||
2410 | 2435 | ||
2411 | team_nl_team_put(team); | 2436 | team_nl_team_put(team); |
2412 | 2437 | ||
@@ -2457,27 +2482,11 @@ static int team_nl_send_event_options_get(struct team *team, | |||
2457 | sel_opt_inst_list); | 2482 | sel_opt_inst_list); |
2458 | } | 2483 | } |
2459 | 2484 | ||
2460 | static int team_nl_send_event_port_list_get(struct team *team) | 2485 | static int team_nl_send_event_port_get(struct team *team, |
2486 | struct team_port *port) | ||
2461 | { | 2487 | { |
2462 | struct sk_buff *skb; | 2488 | return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast, |
2463 | int err; | 2489 | port); |
2464 | struct net *net = dev_net(team->dev); | ||
2465 | |||
2466 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
2467 | if (!skb) | ||
2468 | return -ENOMEM; | ||
2469 | |||
2470 | err = team_nl_fill_port_list_get(skb, 0, 0, 0, team, false); | ||
2471 | if (err < 0) | ||
2472 | goto err_fill; | ||
2473 | |||
2474 | err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id, | ||
2475 | GFP_KERNEL); | ||
2476 | return err; | ||
2477 | |||
2478 | err_fill: | ||
2479 | nlmsg_free(skb); | ||
2480 | return err; | ||
2481 | } | 2490 | } |
2482 | 2491 | ||
2483 | static int team_nl_init(void) | 2492 | static int team_nl_init(void) |
@@ -2550,7 +2559,7 @@ static void __team_port_change_send(struct team_port *port, bool linkup) | |||
2550 | port->state.duplex = 0; | 2559 | port->state.duplex = 0; |
2551 | 2560 | ||
2552 | send_event: | 2561 | send_event: |
2553 | err = team_nl_send_event_port_list_get(port->team); | 2562 | err = team_nl_send_event_port_get(port->team, port); |
2554 | if (err && err != -ESRCH) | 2563 | if (err && err != -ESRCH) |
2555 | netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n", | 2564 | netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n", |
2556 | port->dev->name, err); | 2565 | port->dev->name, err); |