diff options
author | Samuel Mendoza-Jonas <sam@mendozajonas.com> | 2018-11-15 23:51:59 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-11-18 00:09:49 -0500 |
commit | 8d951a75d022d94a05f5fa74217670a981e8302d (patch) | |
tree | d360d9898e404a423779ba2b3ced1a5801515997 /net/ncsi/ncsi-netlink.c | |
parent | 2878a2cfe57a5db21844801cf502fe535a3134b2 (diff) |
net/ncsi: Configure multi-package, multi-channel modes with failover
This patch extends the ncsi-netlink interface with two new commands and
three new attributes to configure multiple packages and/or channels at
once, and configure specific failover modes.
NCSI_CMD_SET_PACKAGE mask and NCSI_CMD_SET_CHANNEL_MASK set a whitelist
of packages or channels allowed to be configured with the
NCSI_ATTR_PACKAGE_MASK and NCSI_ATTR_CHANNEL_MASK attributes
respectively. If one of these whitelists is set only packages or
channels matching the whitelist are considered for the channel queue in
ncsi_choose_active_channel().
These commands may also use the NCSI_ATTR_MULTI_FLAG to signal that
multiple packages or channels may be configured simultaneously. NCSI
hardware arbitration (HWA) must be available in order to enable
multi-package mode. Multi-channel mode is always available.
If the NCSI_ATTR_CHANNEL_ID attribute is present in the
NCSI_CMD_SET_CHANNEL_MASK command the it sets the preferred channel as
with the NCSI_CMD_SET_INTERFACE command. The combination of preferred
channel and channel whitelist defines a primary channel and the allowed
failover channels.
If the NCSI_ATTR_MULTI_FLAG attribute is also present then the preferred
channel is configured for Tx/Rx and the other channels are enabled only
for Rx.
Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ncsi/ncsi-netlink.c')
-rw-r--r-- | net/ncsi/ncsi-netlink.c | 221 |
1 files changed, 193 insertions, 28 deletions
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c index cde48fe43dba..5d782445d2fc 100644 --- a/net/ncsi/ncsi-netlink.c +++ b/net/ncsi/ncsi-netlink.c | |||
@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { | |||
30 | [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, | 30 | [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, |
31 | [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, | 31 | [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, |
32 | [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, | 32 | [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, |
33 | [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, | ||
34 | [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, | ||
35 | [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, | ||
33 | }; | 36 | }; |
34 | 37 | ||
35 | static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) | 38 | static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) |
@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb, | |||
69 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); | 72 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); |
70 | if (nc->state == NCSI_CHANNEL_ACTIVE) | 73 | if (nc->state == NCSI_CHANNEL_ACTIVE) |
71 | nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); | 74 | nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); |
72 | if (ndp->force_channel == nc) | 75 | if (nc == nc->package->preferred_channel) |
73 | nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); | 76 | nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); |
74 | 77 | ||
75 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); | 78 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); |
@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb, | |||
114 | if (!pnest) | 117 | if (!pnest) |
115 | return -ENOMEM; | 118 | return -ENOMEM; |
116 | nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); | 119 | nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); |
117 | if (ndp->force_package == np) | 120 | if ((0x1 << np->id) == ndp->package_whitelist) |
118 | nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); | 121 | nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); |
119 | cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); | 122 | cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); |
120 | if (!cnest) { | 123 | if (!cnest) { |
@@ -290,45 +293,54 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) | |||
290 | package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); | 293 | package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); |
291 | package = NULL; | 294 | package = NULL; |
292 | 295 | ||
293 | spin_lock_irqsave(&ndp->lock, flags); | ||
294 | |||
295 | NCSI_FOR_EACH_PACKAGE(ndp, np) | 296 | NCSI_FOR_EACH_PACKAGE(ndp, np) |
296 | if (np->id == package_id) | 297 | if (np->id == package_id) |
297 | package = np; | 298 | package = np; |
298 | if (!package) { | 299 | if (!package) { |
299 | /* The user has set a package that does not exist */ | 300 | /* The user has set a package that does not exist */ |
300 | spin_unlock_irqrestore(&ndp->lock, flags); | ||
301 | return -ERANGE; | 301 | return -ERANGE; |
302 | } | 302 | } |
303 | 303 | ||
304 | channel = NULL; | 304 | channel = NULL; |
305 | if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { | 305 | if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { |
306 | /* Allow any channel */ | ||
307 | channel_id = NCSI_RESERVED_CHANNEL; | ||
308 | } else { | ||
309 | channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); | 306 | channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); |
310 | NCSI_FOR_EACH_CHANNEL(package, nc) | 307 | NCSI_FOR_EACH_CHANNEL(package, nc) |
311 | if (nc->id == channel_id) | 308 | if (nc->id == channel_id) { |
312 | channel = nc; | 309 | channel = nc; |
310 | break; | ||
311 | } | ||
312 | if (!channel) { | ||
313 | netdev_info(ndp->ndev.dev, | ||
314 | "NCSI: Channel %u does not exist!\n", | ||
315 | channel_id); | ||
316 | return -ERANGE; | ||
317 | } | ||
313 | } | 318 | } |
314 | 319 | ||
315 | if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { | 320 | spin_lock_irqsave(&ndp->lock, flags); |
316 | /* The user has set a channel that does not exist on this | 321 | ndp->package_whitelist = 0x1 << package->id; |
317 | * package | 322 | ndp->multi_package = false; |
318 | */ | ||
319 | spin_unlock_irqrestore(&ndp->lock, flags); | ||
320 | netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", | ||
321 | channel_id); | ||
322 | return -ERANGE; | ||
323 | } | ||
324 | |||
325 | ndp->force_package = package; | ||
326 | ndp->force_channel = channel; | ||
327 | spin_unlock_irqrestore(&ndp->lock, flags); | 323 | spin_unlock_irqrestore(&ndp->lock, flags); |
328 | 324 | ||
329 | netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", | 325 | spin_lock_irqsave(&package->lock, flags); |
330 | package_id, channel_id, | 326 | package->multi_channel = false; |
331 | channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); | 327 | if (channel) { |
328 | package->channel_whitelist = 0x1 << channel->id; | ||
329 | package->preferred_channel = channel; | ||
330 | } else { | ||
331 | /* Allow any channel */ | ||
332 | package->channel_whitelist = UINT_MAX; | ||
333 | package->preferred_channel = NULL; | ||
334 | } | ||
335 | spin_unlock_irqrestore(&package->lock, flags); | ||
336 | |||
337 | if (channel) | ||
338 | netdev_info(ndp->ndev.dev, | ||
339 | "Set package 0x%x, channel 0x%x as preferred\n", | ||
340 | package_id, channel_id); | ||
341 | else | ||
342 | netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", | ||
343 | package_id); | ||
332 | 344 | ||
333 | /* Update channel configuration */ | 345 | /* Update channel configuration */ |
334 | if (!(ndp->flags & NCSI_DEV_RESET)) | 346 | if (!(ndp->flags & NCSI_DEV_RESET)) |
@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) | |||
340 | static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) | 352 | static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) |
341 | { | 353 | { |
342 | struct ncsi_dev_priv *ndp; | 354 | struct ncsi_dev_priv *ndp; |
355 | struct ncsi_package *np; | ||
343 | unsigned long flags; | 356 | unsigned long flags; |
344 | 357 | ||
345 | if (!info || !info->attrs) | 358 | if (!info || !info->attrs) |
@@ -353,11 +366,19 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) | |||
353 | if (!ndp) | 366 | if (!ndp) |
354 | return -ENODEV; | 367 | return -ENODEV; |
355 | 368 | ||
356 | /* Clear any override */ | 369 | /* Reset any whitelists and disable multi mode */ |
357 | spin_lock_irqsave(&ndp->lock, flags); | 370 | spin_lock_irqsave(&ndp->lock, flags); |
358 | ndp->force_package = NULL; | 371 | ndp->package_whitelist = UINT_MAX; |
359 | ndp->force_channel = NULL; | 372 | ndp->multi_package = false; |
360 | spin_unlock_irqrestore(&ndp->lock, flags); | 373 | spin_unlock_irqrestore(&ndp->lock, flags); |
374 | |||
375 | NCSI_FOR_EACH_PACKAGE(ndp, np) { | ||
376 | spin_lock_irqsave(&np->lock, flags); | ||
377 | np->multi_channel = false; | ||
378 | np->channel_whitelist = UINT_MAX; | ||
379 | np->preferred_channel = NULL; | ||
380 | spin_unlock_irqrestore(&np->lock, flags); | ||
381 | } | ||
361 | netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); | 382 | netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); |
362 | 383 | ||
363 | /* Update channel configuration */ | 384 | /* Update channel configuration */ |
@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev, | |||
563 | return nlmsg_unicast(net->genl_sock, skb, snd_portid); | 584 | return nlmsg_unicast(net->genl_sock, skb, snd_portid); |
564 | } | 585 | } |
565 | 586 | ||
587 | static int ncsi_set_package_mask_nl(struct sk_buff *msg, | ||
588 | struct genl_info *info) | ||
589 | { | ||
590 | struct ncsi_dev_priv *ndp; | ||
591 | unsigned long flags; | ||
592 | int rc; | ||
593 | |||
594 | if (!info || !info->attrs) | ||
595 | return -EINVAL; | ||
596 | |||
597 | if (!info->attrs[NCSI_ATTR_IFINDEX]) | ||
598 | return -EINVAL; | ||
599 | |||
600 | if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) | ||
601 | return -EINVAL; | ||
602 | |||
603 | ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), | ||
604 | nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); | ||
605 | if (!ndp) | ||
606 | return -ENODEV; | ||
607 | |||
608 | spin_lock_irqsave(&ndp->lock, flags); | ||
609 | if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { | ||
610 | if (ndp->flags & NCSI_DEV_HWA) { | ||
611 | ndp->multi_package = true; | ||
612 | rc = 0; | ||
613 | } else { | ||
614 | netdev_err(ndp->ndev.dev, | ||
615 | "NCSI: Can't use multiple packages without HWA\n"); | ||
616 | rc = -EPERM; | ||
617 | } | ||
618 | } else { | ||
619 | ndp->multi_package = false; | ||
620 | rc = 0; | ||
621 | } | ||
622 | |||
623 | if (!rc) | ||
624 | ndp->package_whitelist = | ||
625 | nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); | ||
626 | spin_unlock_irqrestore(&ndp->lock, flags); | ||
627 | |||
628 | if (!rc) { | ||
629 | /* Update channel configuration */ | ||
630 | if (!(ndp->flags & NCSI_DEV_RESET)) | ||
631 | ncsi_reset_dev(&ndp->ndev); | ||
632 | } | ||
633 | |||
634 | return rc; | ||
635 | } | ||
636 | |||
637 | static int ncsi_set_channel_mask_nl(struct sk_buff *msg, | ||
638 | struct genl_info *info) | ||
639 | { | ||
640 | struct ncsi_package *np, *package; | ||
641 | struct ncsi_channel *nc, *channel; | ||
642 | u32 package_id, channel_id; | ||
643 | struct ncsi_dev_priv *ndp; | ||
644 | unsigned long flags; | ||
645 | |||
646 | if (!info || !info->attrs) | ||
647 | return -EINVAL; | ||
648 | |||
649 | if (!info->attrs[NCSI_ATTR_IFINDEX]) | ||
650 | return -EINVAL; | ||
651 | |||
652 | if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) | ||
653 | return -EINVAL; | ||
654 | |||
655 | if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) | ||
656 | return -EINVAL; | ||
657 | |||
658 | ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), | ||
659 | nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); | ||
660 | if (!ndp) | ||
661 | return -ENODEV; | ||
662 | |||
663 | package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); | ||
664 | package = NULL; | ||
665 | NCSI_FOR_EACH_PACKAGE(ndp, np) | ||
666 | if (np->id == package_id) { | ||
667 | package = np; | ||
668 | break; | ||
669 | } | ||
670 | if (!package) | ||
671 | return -ERANGE; | ||
672 | |||
673 | spin_lock_irqsave(&package->lock, flags); | ||
674 | |||
675 | channel = NULL; | ||
676 | if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { | ||
677 | channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); | ||
678 | NCSI_FOR_EACH_CHANNEL(np, nc) | ||
679 | if (nc->id == channel_id) { | ||
680 | channel = nc; | ||
681 | break; | ||
682 | } | ||
683 | if (!channel) { | ||
684 | spin_unlock_irqrestore(&package->lock, flags); | ||
685 | return -ERANGE; | ||
686 | } | ||
687 | netdev_dbg(ndp->ndev.dev, | ||
688 | "NCSI: Channel %u set as preferred channel\n", | ||
689 | channel->id); | ||
690 | } | ||
691 | |||
692 | package->channel_whitelist = | ||
693 | nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); | ||
694 | if (package->channel_whitelist == 0) | ||
695 | netdev_dbg(ndp->ndev.dev, | ||
696 | "NCSI: Package %u set to all channels disabled\n", | ||
697 | package->id); | ||
698 | |||
699 | package->preferred_channel = channel; | ||
700 | |||
701 | if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { | ||
702 | package->multi_channel = true; | ||
703 | netdev_info(ndp->ndev.dev, | ||
704 | "NCSI: Multi-channel enabled on package %u\n", | ||
705 | package_id); | ||
706 | } else { | ||
707 | package->multi_channel = false; | ||
708 | } | ||
709 | |||
710 | spin_unlock_irqrestore(&package->lock, flags); | ||
711 | |||
712 | /* Update channel configuration */ | ||
713 | if (!(ndp->flags & NCSI_DEV_RESET)) | ||
714 | ncsi_reset_dev(&ndp->ndev); | ||
715 | |||
716 | return 0; | ||
717 | } | ||
718 | |||
566 | static const struct genl_ops ncsi_ops[] = { | 719 | static const struct genl_ops ncsi_ops[] = { |
567 | { | 720 | { |
568 | .cmd = NCSI_CMD_PKG_INFO, | 721 | .cmd = NCSI_CMD_PKG_INFO, |
@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = { | |||
589 | .doit = ncsi_send_cmd_nl, | 742 | .doit = ncsi_send_cmd_nl, |
590 | .flags = GENL_ADMIN_PERM, | 743 | .flags = GENL_ADMIN_PERM, |
591 | }, | 744 | }, |
745 | { | ||
746 | .cmd = NCSI_CMD_SET_PACKAGE_MASK, | ||
747 | .policy = ncsi_genl_policy, | ||
748 | .doit = ncsi_set_package_mask_nl, | ||
749 | .flags = GENL_ADMIN_PERM, | ||
750 | }, | ||
751 | { | ||
752 | .cmd = NCSI_CMD_SET_CHANNEL_MASK, | ||
753 | .policy = ncsi_genl_policy, | ||
754 | .doit = ncsi_set_channel_mask_nl, | ||
755 | .flags = GENL_ADMIN_PERM, | ||
756 | }, | ||
592 | }; | 757 | }; |
593 | 758 | ||
594 | static struct genl_family ncsi_genl_family __ro_after_init = { | 759 | static struct genl_family ncsi_genl_family __ro_after_init = { |