Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Samuel Mendoza-Jonas and committed by
David S. Miller
8d951a75 2878a2cf

+493 -88
+15
include/uapi/linux/ncsi.h
··· 26 26 * @NCSI_CMD_SEND_CMD: send NC-SI command to network card. 27 27 * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID 28 28 * and NCSI_ATTR_CHANNEL_ID. 29 + * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages. 30 + * Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK. 31 + * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels. 32 + * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and 33 + * NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets 34 + * the primary channel. 29 35 * @NCSI_CMD_MAX: highest command number 30 36 */ 31 37 enum ncsi_nl_commands { ··· 40 34 NCSI_CMD_SET_INTERFACE, 41 35 NCSI_CMD_CLEAR_INTERFACE, 42 36 NCSI_CMD_SEND_CMD, 37 + NCSI_CMD_SET_PACKAGE_MASK, 38 + NCSI_CMD_SET_CHANNEL_MASK, 43 39 44 40 __NCSI_CMD_AFTER_LAST, 45 41 NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 ··· 56 48 * @NCSI_ATTR_PACKAGE_ID: package ID 57 49 * @NCSI_ATTR_CHANNEL_ID: channel ID 58 50 * @NCSI_ATTR_DATA: command payload 51 + * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with 52 + * NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK. 53 + * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages. 54 + * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels. 59 55 * @NCSI_ATTR_MAX: highest attribute number 60 56 */ 61 57 enum ncsi_nl_attrs { ··· 69 57 NCSI_ATTR_PACKAGE_ID, 70 58 NCSI_ATTR_CHANNEL_ID, 71 59 NCSI_ATTR_DATA, 60 + NCSI_ATTR_MULTI_FLAG, 61 + NCSI_ATTR_PACKAGE_MASK, 62 + NCSI_ATTR_CHANNEL_MASK, 72 63 73 64 __NCSI_ATTR_AFTER_LAST, 74 65 NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
+14 -2
net/ncsi/internal.h
··· 222 222 unsigned int channel_num; /* Number of channels */ 223 223 struct list_head channels; /* List of chanels */ 224 224 struct list_head node; /* Form list of packages */ 225 + 226 + bool multi_channel; /* Enable multiple channels */ 227 + u32 channel_whitelist; /* Channels to configure */ 228 + struct ncsi_channel *preferred_channel; /* Primary channel */ 225 229 }; 226 230 227 231 struct ncsi_request { ··· 301 297 unsigned int package_num; /* Number of packages */ 302 298 struct list_head packages; /* List of packages */ 303 299 struct ncsi_channel *hot_channel; /* Channel was ever active */ 304 - struct ncsi_package *force_package; /* Force a specific package */ 305 - struct ncsi_channel *force_channel; /* Force a specific channel */ 306 300 struct ncsi_request requests[256]; /* Request table */ 307 301 unsigned int request_id; /* Last used request ID */ 308 302 #define NCSI_REQ_START_IDX 1 ··· 313 311 struct list_head node; /* Form NCSI device list */ 314 312 #define NCSI_MAX_VLAN_VIDS 15 315 313 struct list_head vlan_vids; /* List of active VLAN IDs */ 314 + 315 + bool multi_package; /* Enable multiple packages */ 316 + u32 package_whitelist; /* Packages to configure */ 316 317 }; 317 318 318 319 struct ncsi_cmd_arg { ··· 369 364 void ncsi_free_request(struct ncsi_request *nr); 370 365 struct ncsi_dev *ncsi_find_dev(struct net_device *dev); 371 366 int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); 367 + bool ncsi_channel_has_link(struct ncsi_channel *channel); 368 + bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, 369 + struct ncsi_channel *channel); 370 + int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, 371 + struct ncsi_package *np, 372 + struct ncsi_channel *disable, 373 + struct ncsi_channel *enable); 372 374 373 375 /* Packet handlers */ 374 376 u32 ncsi_calculate_checksum(unsigned char *data, int len);
+51 -12
net/ncsi/ncsi-aen.c
··· 50 50 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, 51 51 struct ncsi_aen_pkt_hdr *h) 52 52 { 53 - struct ncsi_aen_lsc_pkt *lsc; 54 - struct ncsi_channel *nc; 53 + struct ncsi_channel *nc, *tmp; 55 54 struct ncsi_channel_mode *ncm; 55 + unsigned long old_data, data; 56 + struct ncsi_aen_lsc_pkt *lsc; 57 + struct ncsi_package *np; 58 + bool had_link, has_link; 59 + unsigned long flags; 56 60 bool chained; 57 61 int state; 58 - unsigned long old_data, data; 59 - unsigned long flags; 60 - bool had_link, has_link; 61 62 62 63 /* Find the NCSI channel */ 63 64 ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); ··· 93 92 if ((had_link == has_link) || chained) 94 93 return 0; 95 94 96 - if (had_link) 97 - ndp->flags |= NCSI_DEV_RESHUFFLE; 98 - ncsi_stop_channel_monitor(nc); 99 - spin_lock_irqsave(&ndp->lock, flags); 100 - list_add_tail_rcu(&nc->link, &ndp->channel_queue); 101 - spin_unlock_irqrestore(&ndp->lock, flags); 95 + if (!ndp->multi_package && !nc->package->multi_channel) { 96 + if (had_link) { 97 + ndp->flags |= NCSI_DEV_RESHUFFLE; 98 + ncsi_stop_channel_monitor(nc); 99 + spin_lock_irqsave(&ndp->lock, flags); 100 + list_add_tail_rcu(&nc->link, &ndp->channel_queue); 101 + spin_unlock_irqrestore(&ndp->lock, flags); 102 + return ncsi_process_next_channel(ndp); 103 + } 104 + /* Configured channel came up */ 105 + return 0; 106 + } 102 107 103 - return ncsi_process_next_channel(ndp); 108 + if (had_link) { 109 + ncm = &nc->modes[NCSI_MODE_TX_ENABLE]; 110 + if (ncsi_channel_is_last(ndp, nc)) { 111 + /* No channels left, reconfigure */ 112 + return ncsi_reset_dev(&ndp->ndev); 113 + } else if (ncm->enable) { 114 + /* Need to failover Tx channel */ 115 + ncsi_update_tx_channel(ndp, nc->package, nc, NULL); 116 + } 117 + } else if (has_link && nc->package->preferred_channel == nc) { 118 + /* Return Tx to preferred channel */ 119 + ncsi_update_tx_channel(ndp, nc->package, NULL, nc); 120 + } else if (has_link) { 121 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 122 + NCSI_FOR_EACH_CHANNEL(np, tmp) { 123 + /* Enable Tx on this channel if the current Tx 124 + * channel is down. 125 + */ 126 + ncm = &tmp->modes[NCSI_MODE_TX_ENABLE]; 127 + if (ncm->enable && 128 + !ncsi_channel_has_link(tmp)) { 129 + ncsi_update_tx_channel(ndp, nc->package, 130 + tmp, nc); 131 + break; 132 + } 133 + } 134 + } 135 + } 136 + 137 + /* Leave configured channels active in a multi-channel scenario so 138 + * AEN events are still received. 139 + */ 140 + return 0; 104 141 } 105 142 106 143 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
+219 -45
net/ncsi/ncsi-manage.c
··· 28 28 LIST_HEAD(ncsi_dev_list); 29 29 DEFINE_SPINLOCK(ncsi_dev_lock); 30 30 31 + bool ncsi_channel_has_link(struct ncsi_channel *channel) 32 + { 33 + return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1); 34 + } 35 + 36 + bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, 37 + struct ncsi_channel *channel) 38 + { 39 + struct ncsi_package *np; 40 + struct ncsi_channel *nc; 41 + 42 + NCSI_FOR_EACH_PACKAGE(ndp, np) 43 + NCSI_FOR_EACH_CHANNEL(np, nc) { 44 + if (nc == channel) 45 + continue; 46 + if (nc->state == NCSI_CHANNEL_ACTIVE && 47 + ncsi_channel_has_link(nc)) 48 + return false; 49 + } 50 + 51 + return true; 52 + } 53 + 31 54 static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) 32 55 { 33 56 struct ncsi_dev *nd = &ndp->ndev; ··· 75 52 continue; 76 53 } 77 54 78 - if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { 55 + if (ncsi_channel_has_link(nc)) { 79 56 spin_unlock_irqrestore(&nc->lock, flags); 80 57 nd->link_up = 1; 81 58 goto report; ··· 290 267 np->ndp = ndp; 291 268 spin_lock_init(&np->lock); 292 269 INIT_LIST_HEAD(&np->channels); 270 + np->channel_whitelist = UINT_MAX; 293 271 294 272 spin_lock_irqsave(&ndp->lock, flags); 295 273 tmp = ncsi_find_package(ndp, id); ··· 752 728 753 729 #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ 754 730 731 + /* Determine if a given channel from the channel_queue should be used for Tx */ 732 + static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp, 733 + struct ncsi_channel *nc) 734 + { 735 + struct ncsi_channel_mode *ncm; 736 + struct ncsi_channel *channel; 737 + struct ncsi_package *np; 738 + 739 + /* Check if any other channel has Tx enabled; a channel may have already 740 + * been configured and removed from the channel queue. 741 + */ 742 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 743 + if (!ndp->multi_package && np != nc->package) 744 + continue; 745 + NCSI_FOR_EACH_CHANNEL(np, channel) { 746 + ncm = &channel->modes[NCSI_MODE_TX_ENABLE]; 747 + if (ncm->enable) 748 + return false; 749 + } 750 + } 751 + 752 + /* This channel is the preferred channel and has link */ 753 + list_for_each_entry_rcu(channel, &ndp->channel_queue, link) { 754 + np = channel->package; 755 + if (np->preferred_channel && 756 + ncsi_channel_has_link(np->preferred_channel)) { 757 + return np->preferred_channel == nc; 758 + } 759 + } 760 + 761 + /* This channel has link */ 762 + if (ncsi_channel_has_link(nc)) 763 + return true; 764 + 765 + list_for_each_entry_rcu(channel, &ndp->channel_queue, link) 766 + if (ncsi_channel_has_link(channel)) 767 + return false; 768 + 769 + /* No other channel has link; default to this one */ 770 + return true; 771 + } 772 + 773 + /* Change the active Tx channel in a multi-channel setup */ 774 + int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, 775 + struct ncsi_package *package, 776 + struct ncsi_channel *disable, 777 + struct ncsi_channel *enable) 778 + { 779 + struct ncsi_cmd_arg nca; 780 + struct ncsi_channel *nc; 781 + struct ncsi_package *np; 782 + int ret = 0; 783 + 784 + if (!package->multi_channel && !ndp->multi_package) 785 + netdev_warn(ndp->ndev.dev, 786 + "NCSI: Trying to update Tx channel in single-channel mode\n"); 787 + nca.ndp = ndp; 788 + nca.req_flags = 0; 789 + 790 + /* Find current channel with Tx enabled */ 791 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 792 + if (disable) 793 + break; 794 + if (!ndp->multi_package && np != package) 795 + continue; 796 + 797 + NCSI_FOR_EACH_CHANNEL(np, nc) 798 + if (nc->modes[NCSI_MODE_TX_ENABLE].enable) { 799 + disable = nc; 800 + break; 801 + } 802 + } 803 + 804 + /* Find a suitable channel for Tx */ 805 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 806 + if (enable) 807 + break; 808 + if (!ndp->multi_package && np != package) 809 + continue; 810 + if (!(ndp->package_whitelist & (0x1 << np->id))) 811 + continue; 812 + 813 + if (np->preferred_channel && 814 + ncsi_channel_has_link(np->preferred_channel)) { 815 + enable = np->preferred_channel; 816 + break; 817 + } 818 + 819 + NCSI_FOR_EACH_CHANNEL(np, nc) { 820 + if (!(np->channel_whitelist & 0x1 << nc->id)) 821 + continue; 822 + if (nc->state != NCSI_CHANNEL_ACTIVE) 823 + continue; 824 + if (ncsi_channel_has_link(nc)) { 825 + enable = nc; 826 + break; 827 + } 828 + } 829 + } 830 + 831 + if (disable == enable) 832 + return -1; 833 + 834 + if (!enable) 835 + return -1; 836 + 837 + if (disable) { 838 + nca.channel = disable->id; 839 + nca.package = disable->package->id; 840 + nca.type = NCSI_PKT_CMD_DCNT; 841 + ret = ncsi_xmit_cmd(&nca); 842 + if (ret) 843 + netdev_err(ndp->ndev.dev, 844 + "Error %d sending DCNT\n", 845 + ret); 846 + } 847 + 848 + netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id); 849 + 850 + nca.channel = enable->id; 851 + nca.package = enable->package->id; 852 + nca.type = NCSI_PKT_CMD_ECNT; 853 + ret = ncsi_xmit_cmd(&nca); 854 + if (ret) 855 + netdev_err(ndp->ndev.dev, 856 + "Error %d sending ECNT\n", 857 + ret); 858 + 859 + return ret; 860 + } 861 + 755 862 static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) 756 863 { 757 - struct ncsi_dev *nd = &ndp->ndev; 758 - struct net_device *dev = nd->dev; 759 864 struct ncsi_package *np = ndp->active_package; 760 865 struct ncsi_channel *nc = ndp->active_channel; 761 866 struct ncsi_channel *hot_nc = NULL; 867 + struct ncsi_dev *nd = &ndp->ndev; 868 + struct net_device *dev = nd->dev; 762 869 struct ncsi_cmd_arg nca; 763 870 unsigned char index; 764 871 unsigned long flags; ··· 1011 856 } else if (nd->state == ncsi_dev_state_config_ebf) { 1012 857 nca.type = NCSI_PKT_CMD_EBF; 1013 858 nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; 1014 - nd->state = ncsi_dev_state_config_ecnt; 859 + if (ncsi_channel_is_tx(ndp, nc)) 860 + nd->state = ncsi_dev_state_config_ecnt; 861 + else 862 + nd->state = ncsi_dev_state_config_ec; 1015 863 #if IS_ENABLED(CONFIG_IPV6) 1016 864 if (ndp->inet6_addr_num > 0 && 1017 865 (nc->caps[NCSI_CAP_GENERIC].cap & 1018 866 NCSI_CAP_GENERIC_MC)) 1019 867 nd->state = ncsi_dev_state_config_egmf; 1020 - else 1021 - nd->state = ncsi_dev_state_config_ecnt; 1022 868 } else if (nd->state == ncsi_dev_state_config_egmf) { 1023 869 nca.type = NCSI_PKT_CMD_EGMF; 1024 870 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap; 1025 - nd->state = ncsi_dev_state_config_ecnt; 871 + if (ncsi_channel_is_tx(ndp, nc)) 872 + nd->state = ncsi_dev_state_config_ecnt; 873 + else 874 + nd->state = ncsi_dev_state_config_ec; 1026 875 #endif /* CONFIG_IPV6 */ 1027 876 } else if (nd->state == ncsi_dev_state_config_ecnt) { 877 + if (np->preferred_channel && 878 + nc != np->preferred_channel) 879 + netdev_info(ndp->ndev.dev, 880 + "NCSI: Tx failed over to channel %u\n", 881 + nc->id); 1028 882 nca.type = NCSI_PKT_CMD_ECNT; 1029 883 nd->state = ncsi_dev_state_config_ec; 1030 884 } else if (nd->state == ncsi_dev_state_config_ec) { ··· 1123 959 1124 960 static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) 1125 961 { 1126 - struct ncsi_package *np, *force_package; 1127 - struct ncsi_channel *nc, *found, *hot_nc, *force_channel; 962 + struct ncsi_channel *nc, *found, *hot_nc; 1128 963 struct ncsi_channel_mode *ncm; 1129 - unsigned long flags; 964 + unsigned long flags, cflags; 965 + struct ncsi_package *np; 966 + bool with_link; 1130 967 1131 968 spin_lock_irqsave(&ndp->lock, flags); 1132 969 hot_nc = ndp->hot_channel; 1133 - force_channel = ndp->force_channel; 1134 - force_package = ndp->force_package; 1135 970 spin_unlock_irqrestore(&ndp->lock, flags); 1136 971 1137 - /* Force a specific channel whether or not it has link if we have been 1138 - * configured to do so 1139 - */ 1140 - if (force_package && force_channel) { 1141 - found = force_channel; 1142 - ncm = &found->modes[NCSI_MODE_LINK]; 1143 - if (!(ncm->data[2] & 0x1)) 1144 - netdev_info(ndp->ndev.dev, 1145 - "NCSI: Channel %u forced, but it is link down\n", 1146 - found->id); 1147 - goto out; 1148 - } 1149 - 1150 - /* The search is done once an inactive channel with up 1151 - * link is found. 972 + /* By default the search is done once an inactive channel with up 973 + * link is found, unless a preferred channel is set. 974 + * If multi_package or multi_channel are configured all channels in the 975 + * whitelist are added to the channel queue. 1152 976 */ 1153 977 found = NULL; 978 + with_link = false; 1154 979 NCSI_FOR_EACH_PACKAGE(ndp, np) { 1155 - if (ndp->force_package && np != ndp->force_package) 980 + if (!(ndp->package_whitelist & (0x1 << np->id))) 1156 981 continue; 1157 982 NCSI_FOR_EACH_CHANNEL(np, nc) { 1158 - spin_lock_irqsave(&nc->lock, flags); 983 + if (!(np->channel_whitelist & (0x1 << nc->id))) 984 + continue; 985 + 986 + spin_lock_irqsave(&nc->lock, cflags); 1159 987 1160 988 if (!list_empty(&nc->link) || 1161 989 nc->state != NCSI_CHANNEL_INACTIVE) { 1162 - spin_unlock_irqrestore(&nc->lock, flags); 990 + spin_unlock_irqrestore(&nc->lock, cflags); 1163 991 continue; 1164 992 } 1165 993 ··· 1163 1007 1164 1008 ncm = &nc->modes[NCSI_MODE_LINK]; 1165 1009 if (ncm->data[2] & 0x1) { 1166 - spin_unlock_irqrestore(&nc->lock, flags); 1167 1010 found = nc; 1168 - goto out; 1011 + with_link = true; 1169 1012 } 1170 1013 1171 - spin_unlock_irqrestore(&nc->lock, flags); 1014 + /* If multi_channel is enabled configure all valid 1015 + * channels whether or not they currently have link 1016 + * so they will have AENs enabled. 1017 + */ 1018 + if (with_link || np->multi_channel) { 1019 + spin_lock_irqsave(&ndp->lock, flags); 1020 + list_add_tail_rcu(&nc->link, 1021 + &ndp->channel_queue); 1022 + spin_unlock_irqrestore(&ndp->lock, flags); 1023 + 1024 + netdev_dbg(ndp->ndev.dev, 1025 + "NCSI: Channel %u added to queue (link %s)\n", 1026 + nc->id, 1027 + ncm->data[2] & 0x1 ? "up" : "down"); 1028 + } 1029 + 1030 + spin_unlock_irqrestore(&nc->lock, cflags); 1031 + 1032 + if (with_link && !np->multi_channel) 1033 + break; 1172 1034 } 1035 + if (with_link && !ndp->multi_package) 1036 + break; 1173 1037 } 1174 1038 1175 - if (!found) { 1039 + if (list_empty(&ndp->channel_queue) && found) { 1040 + netdev_info(ndp->ndev.dev, 1041 + "NCSI: No channel with link found, configuring channel %u\n", 1042 + found->id); 1043 + spin_lock_irqsave(&ndp->lock, flags); 1044 + list_add_tail_rcu(&found->link, &ndp->channel_queue); 1045 + spin_unlock_irqrestore(&ndp->lock, flags); 1046 + } else if (!found) { 1176 1047 netdev_warn(ndp->ndev.dev, 1177 - "NCSI: No channel found with link\n"); 1048 + "NCSI: No channel found to configure!\n"); 1178 1049 ncsi_report_link(ndp, true); 1179 1050 return -ENODEV; 1180 1051 } 1181 - 1182 - ncm = &found->modes[NCSI_MODE_LINK]; 1183 - netdev_dbg(ndp->ndev.dev, 1184 - "NCSI: Channel %u added to queue (link %s)\n", 1185 - found->id, ncm->data[2] & 0x1 ? "up" : "down"); 1186 - 1187 - out: 1188 - spin_lock_irqsave(&ndp->lock, flags); 1189 - list_add_tail_rcu(&found->link, &ndp->channel_queue); 1190 - spin_unlock_irqrestore(&ndp->lock, flags); 1191 1052 1192 1053 return ncsi_process_next_channel(ndp); 1193 1054 } ··· 1690 1517 INIT_LIST_HEAD(&ndp->channel_queue); 1691 1518 INIT_LIST_HEAD(&ndp->vlan_vids); 1692 1519 INIT_WORK(&ndp->work, ncsi_dev_work); 1520 + ndp->package_whitelist = UINT_MAX; 1693 1521 1694 1522 /* Initialize private NCSI device */ 1695 1523 spin_lock_init(&ndp->lock);
+193 -28
net/ncsi/ncsi-netlink.c
··· 30 30 [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, 31 31 [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, 32 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 38 static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) ··· 72 69 nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 73 70 if (nc->state == NCSI_CHANNEL_ACTIVE) 74 71 nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 75 - if (ndp->force_channel == nc) 72 + if (nc == nc->package->preferred_channel) 76 73 nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 77 74 78 75 nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); ··· 117 114 if (!pnest) 118 115 return -ENOMEM; 119 116 nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 120 - if (ndp->force_package == np) 117 + if ((0x1 << np->id) == ndp->package_whitelist) 121 118 nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 122 119 cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 123 120 if (!cnest) { ··· 293 290 package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 294 291 package = NULL; 295 292 296 - spin_lock_irqsave(&ndp->lock, flags); 297 - 298 293 NCSI_FOR_EACH_PACKAGE(ndp, np) 299 294 if (np->id == package_id) 300 295 package = np; 301 296 if (!package) { 302 297 /* The user has set a package that does not exist */ 303 - spin_unlock_irqrestore(&ndp->lock, flags); 304 298 return -ERANGE; 305 299 } 306 300 307 301 channel = NULL; 308 - if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 309 - /* Allow any channel */ 310 - channel_id = NCSI_RESERVED_CHANNEL; 311 - } else { 302 + if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 312 303 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 313 304 NCSI_FOR_EACH_CHANNEL(package, nc) 314 - if (nc->id == channel_id) 305 + if (nc->id == channel_id) { 315 306 channel = nc; 307 + break; 308 + } 309 + if (!channel) { 310 + netdev_info(ndp->ndev.dev, 311 + "NCSI: Channel %u does not exist!\n", 312 + channel_id); 313 + return -ERANGE; 314 + } 316 315 } 317 316 318 - if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { 319 - /* The user has set a channel that does not exist on this 320 - * package 321 - */ 322 - spin_unlock_irqrestore(&ndp->lock, flags); 323 - netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", 324 - channel_id); 325 - return -ERANGE; 326 - } 327 - 328 - ndp->force_package = package; 329 - ndp->force_channel = channel; 317 + spin_lock_irqsave(&ndp->lock, flags); 318 + ndp->package_whitelist = 0x1 << package->id; 319 + ndp->multi_package = false; 330 320 spin_unlock_irqrestore(&ndp->lock, flags); 331 321 332 - netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", 333 - package_id, channel_id, 334 - channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); 322 + spin_lock_irqsave(&package->lock, flags); 323 + package->multi_channel = false; 324 + if (channel) { 325 + package->channel_whitelist = 0x1 << channel->id; 326 + package->preferred_channel = channel; 327 + } else { 328 + /* Allow any channel */ 329 + package->channel_whitelist = UINT_MAX; 330 + package->preferred_channel = NULL; 331 + } 332 + spin_unlock_irqrestore(&package->lock, flags); 333 + 334 + if (channel) 335 + netdev_info(ndp->ndev.dev, 336 + "Set package 0x%x, channel 0x%x as preferred\n", 337 + package_id, channel_id); 338 + else 339 + netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", 340 + package_id); 335 341 336 342 /* Update channel configuration */ 337 343 if (!(ndp->flags & NCSI_DEV_RESET)) ··· 352 340 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 353 341 { 354 342 struct ncsi_dev_priv *ndp; 343 + struct ncsi_package *np; 355 344 unsigned long flags; 356 345 357 346 if (!info || !info->attrs) ··· 366 353 if (!ndp) 367 354 return -ENODEV; 368 355 369 - /* Clear any override */ 356 + /* Reset any whitelists and disable multi mode */ 370 357 spin_lock_irqsave(&ndp->lock, flags); 371 - ndp->force_package = NULL; 372 - ndp->force_channel = NULL; 358 + ndp->package_whitelist = UINT_MAX; 359 + ndp->multi_package = false; 373 360 spin_unlock_irqrestore(&ndp->lock, flags); 361 + 362 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 363 + spin_lock_irqsave(&np->lock, flags); 364 + np->multi_channel = false; 365 + np->channel_whitelist = UINT_MAX; 366 + np->preferred_channel = NULL; 367 + spin_unlock_irqrestore(&np->lock, flags); 368 + } 374 369 netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 375 370 376 371 /* Update channel configuration */ ··· 584 563 return nlmsg_unicast(net->genl_sock, skb, snd_portid); 585 564 } 586 565 566 + static int ncsi_set_package_mask_nl(struct sk_buff *msg, 567 + struct genl_info *info) 568 + { 569 + struct ncsi_dev_priv *ndp; 570 + unsigned long flags; 571 + int rc; 572 + 573 + if (!info || !info->attrs) 574 + return -EINVAL; 575 + 576 + if (!info->attrs[NCSI_ATTR_IFINDEX]) 577 + return -EINVAL; 578 + 579 + if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) 580 + return -EINVAL; 581 + 582 + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 583 + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 584 + if (!ndp) 585 + return -ENODEV; 586 + 587 + spin_lock_irqsave(&ndp->lock, flags); 588 + if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 589 + if (ndp->flags & NCSI_DEV_HWA) { 590 + ndp->multi_package = true; 591 + rc = 0; 592 + } else { 593 + netdev_err(ndp->ndev.dev, 594 + "NCSI: Can't use multiple packages without HWA\n"); 595 + rc = -EPERM; 596 + } 597 + } else { 598 + ndp->multi_package = false; 599 + rc = 0; 600 + } 601 + 602 + if (!rc) 603 + ndp->package_whitelist = 604 + nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); 605 + spin_unlock_irqrestore(&ndp->lock, flags); 606 + 607 + if (!rc) { 608 + /* Update channel configuration */ 609 + if (!(ndp->flags & NCSI_DEV_RESET)) 610 + ncsi_reset_dev(&ndp->ndev); 611 + } 612 + 613 + return rc; 614 + } 615 + 616 + static int ncsi_set_channel_mask_nl(struct sk_buff *msg, 617 + struct genl_info *info) 618 + { 619 + struct ncsi_package *np, *package; 620 + struct ncsi_channel *nc, *channel; 621 + u32 package_id, channel_id; 622 + struct ncsi_dev_priv *ndp; 623 + unsigned long flags; 624 + 625 + if (!info || !info->attrs) 626 + return -EINVAL; 627 + 628 + if (!info->attrs[NCSI_ATTR_IFINDEX]) 629 + return -EINVAL; 630 + 631 + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 632 + return -EINVAL; 633 + 634 + if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) 635 + return -EINVAL; 636 + 637 + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 638 + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 639 + if (!ndp) 640 + return -ENODEV; 641 + 642 + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 643 + package = NULL; 644 + NCSI_FOR_EACH_PACKAGE(ndp, np) 645 + if (np->id == package_id) { 646 + package = np; 647 + break; 648 + } 649 + if (!package) 650 + return -ERANGE; 651 + 652 + spin_lock_irqsave(&package->lock, flags); 653 + 654 + channel = NULL; 655 + if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 656 + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 657 + NCSI_FOR_EACH_CHANNEL(np, nc) 658 + if (nc->id == channel_id) { 659 + channel = nc; 660 + break; 661 + } 662 + if (!channel) { 663 + spin_unlock_irqrestore(&package->lock, flags); 664 + return -ERANGE; 665 + } 666 + netdev_dbg(ndp->ndev.dev, 667 + "NCSI: Channel %u set as preferred channel\n", 668 + channel->id); 669 + } 670 + 671 + package->channel_whitelist = 672 + nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); 673 + if (package->channel_whitelist == 0) 674 + netdev_dbg(ndp->ndev.dev, 675 + "NCSI: Package %u set to all channels disabled\n", 676 + package->id); 677 + 678 + package->preferred_channel = channel; 679 + 680 + if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 681 + package->multi_channel = true; 682 + netdev_info(ndp->ndev.dev, 683 + "NCSI: Multi-channel enabled on package %u\n", 684 + package_id); 685 + } else { 686 + package->multi_channel = false; 687 + } 688 + 689 + spin_unlock_irqrestore(&package->lock, flags); 690 + 691 + /* Update channel configuration */ 692 + if (!(ndp->flags & NCSI_DEV_RESET)) 693 + ncsi_reset_dev(&ndp->ndev); 694 + 695 + return 0; 696 + } 697 + 587 698 static const struct genl_ops ncsi_ops[] = { 588 699 { 589 700 .cmd = NCSI_CMD_PKG_INFO, ··· 740 587 .cmd = NCSI_CMD_SEND_CMD, 741 588 .policy = ncsi_genl_policy, 742 589 .doit = ncsi_send_cmd_nl, 590 + .flags = GENL_ADMIN_PERM, 591 + }, 592 + { 593 + .cmd = NCSI_CMD_SET_PACKAGE_MASK, 594 + .policy = ncsi_genl_policy, 595 + .doit = ncsi_set_package_mask_nl, 596 + .flags = GENL_ADMIN_PERM, 597 + }, 598 + { 599 + .cmd = NCSI_CMD_SET_CHANNEL_MASK, 600 + .policy = ncsi_genl_policy, 601 + .doit = ncsi_set_channel_mask_nl, 743 602 .flags = GENL_ADMIN_PERM, 744 603 }, 745 604 };
+1 -1
net/ncsi/ncsi-rsp.c
··· 256 256 if (!ncm->enable) 257 257 return 0; 258 258 259 - ncm->enable = 1; 259 + ncm->enable = 0; 260 260 return 0; 261 261 } 262 262