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

net/ncsi: Resource management

NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.

* The user (e.g. netdev driver) dereference NCSI device by
"struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
The later one is used by NCSI stack internally.
* Every NCSI device can have multiple packages simultaneously, up
to 8 packages. It's represented by "struct ncsi_package" and
identified by 3-bits ID.
* Every NCSI package can have multiple channels, up to 32. It's
represented by "struct ncsi_channel" and identified by 5-bits ID.
* Every NCSI channel has version, statistics, various modes and
filters. They are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.

link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Gavin Shan and committed by
David S. Miller
2d283bdd 5e31c701

+756
+46
include/net/ncsi.h
··· 1 + #ifndef __NET_NCSI_H 2 + #define __NET_NCSI_H 3 + 4 + /* 5 + * The NCSI device states seen from external. More NCSI device states are 6 + * only visible internally (in net/ncsi/internal.h). When the NCSI device 7 + * is registered, it's in ncsi_dev_state_registered state. The state 8 + * ncsi_dev_state_start is used to drive to choose active package and 9 + * channel. After that, its state is changed to ncsi_dev_state_functional. 10 + * 11 + * The state ncsi_dev_state_stop helps to shut down the currently active 12 + * package and channel while ncsi_dev_state_config helps to reconfigure 13 + * them. 14 + */ 15 + enum { 16 + ncsi_dev_state_registered = 0x0000, 17 + ncsi_dev_state_functional = 0x0100, 18 + ncsi_dev_state_probe = 0x0200, 19 + ncsi_dev_state_config = 0x0300, 20 + ncsi_dev_state_suspend = 0x0400, 21 + }; 22 + 23 + struct ncsi_dev { 24 + int state; 25 + int link_up; 26 + struct net_device *dev; 27 + void (*handler)(struct ncsi_dev *ndev); 28 + }; 29 + 30 + #ifdef CONFIG_NET_NCSI 31 + struct ncsi_dev *ncsi_register_dev(struct net_device *dev, 32 + void (*notifier)(struct ncsi_dev *nd)); 33 + void ncsi_unregister_dev(struct ncsi_dev *nd); 34 + #else /* !CONFIG_NET_NCSI */ 35 + static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev, 36 + void (*notifier)(struct ncsi_dev *nd)) 37 + { 38 + return NULL; 39 + } 40 + 41 + static inline void ncsi_unregister_dev(struct ncsi_dev *nd) 42 + { 43 + } 44 + #endif /* CONFIG_NET_NCSI */ 45 + 46 + #endif /* __NET_NCSI_H */
+1
net/Kconfig
··· 237 237 source "net/switchdev/Kconfig" 238 238 source "net/l3mdev/Kconfig" 239 239 source "net/qrtr/Kconfig" 240 + source "net/ncsi/Kconfig" 240 241 241 242 config RPS 242 243 bool
+1
net/Makefile
··· 79 79 obj-y += l3mdev/ 80 80 endif 81 81 obj-$(CONFIG_QRTR) += qrtr/ 82 + obj-$(CONFIG_NET_NCSI) += ncsi/
+12
net/ncsi/Kconfig
··· 1 + # 2 + # Configuration for NCSI support 3 + # 4 + 5 + config NET_NCSI 6 + bool "NCSI interface support" 7 + depends on INET 8 + ---help--- 9 + This module provides NCSI (Network Controller Sideband Interface) 10 + support. Enable this only if your system connects to a network 11 + device via NCSI and the ethernet driver you're using supports 12 + the protocol explicitly.
+4
net/ncsi/Makefile
··· 1 + # 2 + # Makefile for NCSI API 3 + # 4 + obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
+256
net/ncsi/internal.h
··· 1 + /* 2 + * Copyright Gavin Shan, IBM Corporation 2016. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #ifndef __NCSI_INTERNAL_H__ 11 + #define __NCSI_INTERNAL_H__ 12 + 13 + enum { 14 + NCSI_CAP_BASE = 0, 15 + NCSI_CAP_GENERIC = 0, 16 + NCSI_CAP_BC, 17 + NCSI_CAP_MC, 18 + NCSI_CAP_BUFFER, 19 + NCSI_CAP_AEN, 20 + NCSI_CAP_VLAN, 21 + NCSI_CAP_MAX 22 + }; 23 + 24 + enum { 25 + NCSI_CAP_GENERIC_HWA = 0x01, /* HW arbitration */ 26 + NCSI_CAP_GENERIC_HDS = 0x02, /* HNC driver status change */ 27 + NCSI_CAP_GENERIC_FC = 0x04, /* HNC to MC flow control */ 28 + NCSI_CAP_GENERIC_FC1 = 0x08, /* MC to HNC flow control */ 29 + NCSI_CAP_GENERIC_MC = 0x10, /* Global MC filtering */ 30 + NCSI_CAP_GENERIC_HWA_UNKNOWN = 0x00, /* Unknown HW arbitration */ 31 + NCSI_CAP_GENERIC_HWA_SUPPORT = 0x20, /* Supported HW arbitration */ 32 + NCSI_CAP_GENERIC_HWA_NOT_SUPPORT = 0x40, /* No HW arbitration */ 33 + NCSI_CAP_GENERIC_HWA_RESERVED = 0x60, /* Reserved HW arbitration */ 34 + NCSI_CAP_GENERIC_HWA_MASK = 0x60, /* Mask for HW arbitration */ 35 + NCSI_CAP_GENERIC_MASK = 0x7f, 36 + NCSI_CAP_BC_ARP = 0x01, /* ARP packet filtering */ 37 + NCSI_CAP_BC_DHCPC = 0x02, /* DHCP client filtering */ 38 + NCSI_CAP_BC_DHCPS = 0x04, /* DHCP server filtering */ 39 + NCSI_CAP_BC_NETBIOS = 0x08, /* NetBIOS packet filtering */ 40 + NCSI_CAP_BC_MASK = 0x0f, 41 + NCSI_CAP_MC_IPV6_NEIGHBOR = 0x01, /* IPv6 neighbor filtering */ 42 + NCSI_CAP_MC_IPV6_ROUTER = 0x02, /* IPv6 router filering */ 43 + NCSI_CAP_MC_DHCPV6_RELAY = 0x04, /* DHCPv6 relay / server MC */ 44 + NCSI_CAP_MC_DHCPV6_WELL_KNOWN = 0x08, /* DHCPv6 well-known MC */ 45 + NCSI_CAP_MC_IPV6_MLD = 0x10, /* IPv6 MLD filtering */ 46 + NCSI_CAP_MC_IPV6_NEIGHBOR_S = 0x20, /* IPv6 neighbour filtering */ 47 + NCSI_CAP_MC_MASK = 0x3f, 48 + NCSI_CAP_AEN_LSC = 0x01, /* Link status change */ 49 + NCSI_CAP_AEN_CR = 0x02, /* Configuration required */ 50 + NCSI_CAP_AEN_HDS = 0x04, /* HNC driver status */ 51 + NCSI_CAP_AEN_MASK = 0x07, 52 + NCSI_CAP_VLAN_ONLY = 0x01, /* Filter VLAN packet only */ 53 + NCSI_CAP_VLAN_NO = 0x02, /* Filter VLAN and non-VLAN */ 54 + NCSI_CAP_VLAN_ANY = 0x04, /* Filter Any-and-non-VLAN */ 55 + NCSI_CAP_VLAN_MASK = 0x07 56 + }; 57 + 58 + enum { 59 + NCSI_MODE_BASE = 0, 60 + NCSI_MODE_ENABLE = 0, 61 + NCSI_MODE_TX_ENABLE, 62 + NCSI_MODE_LINK, 63 + NCSI_MODE_VLAN, 64 + NCSI_MODE_BC, 65 + NCSI_MODE_MC, 66 + NCSI_MODE_AEN, 67 + NCSI_MODE_FC, 68 + NCSI_MODE_MAX 69 + }; 70 + 71 + enum { 72 + NCSI_FILTER_BASE = 0, 73 + NCSI_FILTER_VLAN = 0, 74 + NCSI_FILTER_UC, 75 + NCSI_FILTER_MC, 76 + NCSI_FILTER_MIXED, 77 + NCSI_FILTER_MAX 78 + }; 79 + 80 + struct ncsi_channel_version { 81 + u32 version; /* Supported BCD encoded NCSI version */ 82 + u32 alpha2; /* Supported BCD encoded NCSI version */ 83 + u8 fw_name[12]; /* Firware name string */ 84 + u32 fw_version; /* Firmware version */ 85 + u16 pci_ids[4]; /* PCI identification */ 86 + u32 mf_id; /* Manufacture ID */ 87 + }; 88 + 89 + struct ncsi_channel_cap { 90 + u32 index; /* Index of channel capabilities */ 91 + u32 cap; /* NCSI channel capability */ 92 + }; 93 + 94 + struct ncsi_channel_mode { 95 + u32 index; /* Index of channel modes */ 96 + u32 enable; /* Enabled or disabled */ 97 + u32 size; /* Valid entries in ncm_data[] */ 98 + u32 data[8]; /* Data entries */ 99 + }; 100 + 101 + struct ncsi_channel_filter { 102 + u32 index; /* Index of channel filters */ 103 + u32 total; /* Total entries in the filter table */ 104 + u64 bitmap; /* Bitmap of valid entries */ 105 + u32 data[]; /* Data for the valid entries */ 106 + }; 107 + 108 + struct ncsi_channel_stats { 109 + u32 hnc_cnt_hi; /* Counter cleared */ 110 + u32 hnc_cnt_lo; /* Counter cleared */ 111 + u32 hnc_rx_bytes; /* Rx bytes */ 112 + u32 hnc_tx_bytes; /* Tx bytes */ 113 + u32 hnc_rx_uc_pkts; /* Rx UC packets */ 114 + u32 hnc_rx_mc_pkts; /* Rx MC packets */ 115 + u32 hnc_rx_bc_pkts; /* Rx BC packets */ 116 + u32 hnc_tx_uc_pkts; /* Tx UC packets */ 117 + u32 hnc_tx_mc_pkts; /* Tx MC packets */ 118 + u32 hnc_tx_bc_pkts; /* Tx BC packets */ 119 + u32 hnc_fcs_err; /* FCS errors */ 120 + u32 hnc_align_err; /* Alignment errors */ 121 + u32 hnc_false_carrier; /* False carrier detection */ 122 + u32 hnc_runt_pkts; /* Rx runt packets */ 123 + u32 hnc_jabber_pkts; /* Rx jabber packets */ 124 + u32 hnc_rx_pause_xon; /* Rx pause XON frames */ 125 + u32 hnc_rx_pause_xoff; /* Rx XOFF frames */ 126 + u32 hnc_tx_pause_xon; /* Tx XON frames */ 127 + u32 hnc_tx_pause_xoff; /* Tx XOFF frames */ 128 + u32 hnc_tx_s_collision; /* Single collision frames */ 129 + u32 hnc_tx_m_collision; /* Multiple collision frames */ 130 + u32 hnc_l_collision; /* Late collision frames */ 131 + u32 hnc_e_collision; /* Excessive collision frames */ 132 + u32 hnc_rx_ctl_frames; /* Rx control frames */ 133 + u32 hnc_rx_64_frames; /* Rx 64-bytes frames */ 134 + u32 hnc_rx_127_frames; /* Rx 65-127 bytes frames */ 135 + u32 hnc_rx_255_frames; /* Rx 128-255 bytes frames */ 136 + u32 hnc_rx_511_frames; /* Rx 256-511 bytes frames */ 137 + u32 hnc_rx_1023_frames; /* Rx 512-1023 bytes frames */ 138 + u32 hnc_rx_1522_frames; /* Rx 1024-1522 bytes frames */ 139 + u32 hnc_rx_9022_frames; /* Rx 1523-9022 bytes frames */ 140 + u32 hnc_tx_64_frames; /* Tx 64-bytes frames */ 141 + u32 hnc_tx_127_frames; /* Tx 65-127 bytes frames */ 142 + u32 hnc_tx_255_frames; /* Tx 128-255 bytes frames */ 143 + u32 hnc_tx_511_frames; /* Tx 256-511 bytes frames */ 144 + u32 hnc_tx_1023_frames; /* Tx 512-1023 bytes frames */ 145 + u32 hnc_tx_1522_frames; /* Tx 1024-1522 bytes frames */ 146 + u32 hnc_tx_9022_frames; /* Tx 1523-9022 bytes frames */ 147 + u32 hnc_rx_valid_bytes; /* Rx valid bytes */ 148 + u32 hnc_rx_runt_pkts; /* Rx error runt packets */ 149 + u32 hnc_rx_jabber_pkts; /* Rx error jabber packets */ 150 + u32 ncsi_rx_cmds; /* Rx NCSI commands */ 151 + u32 ncsi_dropped_cmds; /* Dropped commands */ 152 + u32 ncsi_cmd_type_errs; /* Command type errors */ 153 + u32 ncsi_cmd_csum_errs; /* Command checksum errors */ 154 + u32 ncsi_rx_pkts; /* Rx NCSI packets */ 155 + u32 ncsi_tx_pkts; /* Tx NCSI packets */ 156 + u32 ncsi_tx_aen_pkts; /* Tx AEN packets */ 157 + u32 pt_tx_pkts; /* Tx packets */ 158 + u32 pt_tx_dropped; /* Tx dropped packets */ 159 + u32 pt_tx_channel_err; /* Tx channel errors */ 160 + u32 pt_tx_us_err; /* Tx undersize errors */ 161 + u32 pt_rx_pkts; /* Rx packets */ 162 + u32 pt_rx_dropped; /* Rx dropped packets */ 163 + u32 pt_rx_channel_err; /* Rx channel errors */ 164 + u32 pt_rx_us_err; /* Rx undersize errors */ 165 + u32 pt_rx_os_err; /* Rx oversize errors */ 166 + }; 167 + 168 + struct ncsi_dev_priv; 169 + struct ncsi_package; 170 + 171 + #define NCSI_PACKAGE_SHIFT 5 172 + #define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7) 173 + #define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1)) 174 + #define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c)) 175 + 176 + struct ncsi_channel { 177 + unsigned char id; 178 + int state; 179 + #define NCSI_CHANNEL_INACTIVE 1 180 + #define NCSI_CHANNEL_ACTIVE 2 181 + spinlock_t lock; /* Protect filters etc */ 182 + struct ncsi_package *package; 183 + struct ncsi_channel_version version; 184 + struct ncsi_channel_cap caps[NCSI_CAP_MAX]; 185 + struct ncsi_channel_mode modes[NCSI_MODE_MAX]; 186 + struct ncsi_channel_filter *filters[NCSI_FILTER_MAX]; 187 + struct ncsi_channel_stats stats; 188 + struct list_head node; 189 + }; 190 + 191 + struct ncsi_package { 192 + unsigned char id; /* NCSI 3-bits package ID */ 193 + unsigned char uuid[16]; /* UUID */ 194 + struct ncsi_dev_priv *ndp; /* NCSI device */ 195 + spinlock_t lock; /* Protect the package */ 196 + unsigned int channel_num; /* Number of channels */ 197 + struct list_head channels; /* List of chanels */ 198 + struct list_head node; /* Form list of packages */ 199 + }; 200 + 201 + struct ncsi_request { 202 + unsigned char id; /* Request ID - 0 to 255 */ 203 + bool used; /* Request that has been assigned */ 204 + bool driven; /* Drive state machine */ 205 + struct ncsi_dev_priv *ndp; /* Associated NCSI device */ 206 + struct sk_buff *cmd; /* Associated NCSI command packet */ 207 + struct sk_buff *rsp; /* Associated NCSI response packet */ 208 + struct timer_list timer; /* Timer on waiting for response */ 209 + bool enabled; /* Time has been enabled or not */ 210 + }; 211 + 212 + struct ncsi_dev_priv { 213 + struct ncsi_dev ndev; /* Associated NCSI device */ 214 + unsigned int flags; /* NCSI device flags */ 215 + spinlock_t lock; /* Protect the NCSI device */ 216 + unsigned int package_num; /* Number of packages */ 217 + struct list_head packages; /* List of packages */ 218 + struct ncsi_request requests[256]; /* Request table */ 219 + unsigned int request_id; /* Last used request ID */ 220 + struct list_head node; /* Form NCSI device list */ 221 + }; 222 + 223 + extern struct list_head ncsi_dev_list; 224 + extern spinlock_t ncsi_dev_lock; 225 + 226 + #define TO_NCSI_DEV_PRIV(nd) \ 227 + container_of(nd, struct ncsi_dev_priv, ndev) 228 + #define NCSI_FOR_EACH_DEV(ndp) \ 229 + list_for_each_entry_rcu(ndp, &ncsi_dev_list, node) 230 + #define NCSI_FOR_EACH_PACKAGE(ndp, np) \ 231 + list_for_each_entry_rcu(np, &ndp->packages, node) 232 + #define NCSI_FOR_EACH_CHANNEL(np, nc) \ 233 + list_for_each_entry_rcu(nc, &np->channels, node) 234 + 235 + /* Resources */ 236 + int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data); 237 + int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data); 238 + int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index); 239 + struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, 240 + unsigned char id); 241 + struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, 242 + unsigned char id); 243 + struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp, 244 + unsigned char id); 245 + struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp, 246 + unsigned char id); 247 + void ncsi_remove_package(struct ncsi_package *np); 248 + void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp, 249 + unsigned char id, 250 + struct ncsi_package **np, 251 + struct ncsi_channel **nc); 252 + struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven); 253 + void ncsi_free_request(struct ncsi_request *nr); 254 + struct ncsi_dev *ncsi_find_dev(struct net_device *dev); 255 + 256 + #endif /* __NCSI_INTERNAL_H__ */
+436
net/ncsi/ncsi-manage.c
··· 1 + /* 2 + * Copyright Gavin Shan, IBM Corporation 2016. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #include <linux/module.h> 11 + #include <linux/kernel.h> 12 + #include <linux/init.h> 13 + #include <linux/netdevice.h> 14 + #include <linux/skbuff.h> 15 + #include <linux/netlink.h> 16 + 17 + #include <net/ncsi.h> 18 + #include <net/net_namespace.h> 19 + #include <net/sock.h> 20 + 21 + #include "internal.h" 22 + 23 + LIST_HEAD(ncsi_dev_list); 24 + DEFINE_SPINLOCK(ncsi_dev_lock); 25 + 26 + static inline int ncsi_filter_size(int table) 27 + { 28 + int sizes[] = { 2, 6, 6, 6 }; 29 + 30 + BUILD_BUG_ON(ARRAY_SIZE(sizes) != NCSI_FILTER_MAX); 31 + if (table < NCSI_FILTER_BASE || table >= NCSI_FILTER_MAX) 32 + return -EINVAL; 33 + 34 + return sizes[table]; 35 + } 36 + 37 + int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data) 38 + { 39 + struct ncsi_channel_filter *ncf; 40 + void *bitmap; 41 + int index, size; 42 + unsigned long flags; 43 + 44 + ncf = nc->filters[table]; 45 + if (!ncf) 46 + return -ENXIO; 47 + 48 + size = ncsi_filter_size(table); 49 + if (size < 0) 50 + return size; 51 + 52 + spin_lock_irqsave(&nc->lock, flags); 53 + bitmap = (void *)&ncf->bitmap; 54 + index = -1; 55 + while ((index = find_next_bit(bitmap, ncf->total, index + 1)) 56 + < ncf->total) { 57 + if (!memcmp(ncf->data + size * index, data, size)) { 58 + spin_unlock_irqrestore(&nc->lock, flags); 59 + return index; 60 + } 61 + } 62 + spin_unlock_irqrestore(&nc->lock, flags); 63 + 64 + return -ENOENT; 65 + } 66 + 67 + int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data) 68 + { 69 + struct ncsi_channel_filter *ncf; 70 + int index, size; 71 + void *bitmap; 72 + unsigned long flags; 73 + 74 + size = ncsi_filter_size(table); 75 + if (size < 0) 76 + return size; 77 + 78 + index = ncsi_find_filter(nc, table, data); 79 + if (index >= 0) 80 + return index; 81 + 82 + ncf = nc->filters[table]; 83 + if (!ncf) 84 + return -ENODEV; 85 + 86 + spin_lock_irqsave(&nc->lock, flags); 87 + bitmap = (void *)&ncf->bitmap; 88 + do { 89 + index = find_next_zero_bit(bitmap, ncf->total, 0); 90 + if (index >= ncf->total) { 91 + spin_unlock_irqrestore(&nc->lock, flags); 92 + return -ENOSPC; 93 + } 94 + } while (test_and_set_bit(index, bitmap)); 95 + 96 + memcpy(ncf->data + size * index, data, size); 97 + spin_unlock_irqrestore(&nc->lock, flags); 98 + 99 + return index; 100 + } 101 + 102 + int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index) 103 + { 104 + struct ncsi_channel_filter *ncf; 105 + int size; 106 + void *bitmap; 107 + unsigned long flags; 108 + 109 + size = ncsi_filter_size(table); 110 + if (size < 0) 111 + return size; 112 + 113 + ncf = nc->filters[table]; 114 + if (!ncf || index >= ncf->total) 115 + return -ENODEV; 116 + 117 + spin_lock_irqsave(&nc->lock, flags); 118 + bitmap = (void *)&ncf->bitmap; 119 + if (test_and_clear_bit(index, bitmap)) 120 + memset(ncf->data + size * index, 0, size); 121 + spin_unlock_irqrestore(&nc->lock, flags); 122 + 123 + return 0; 124 + } 125 + 126 + struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, 127 + unsigned char id) 128 + { 129 + struct ncsi_channel *nc; 130 + 131 + NCSI_FOR_EACH_CHANNEL(np, nc) { 132 + if (nc->id == id) 133 + return nc; 134 + } 135 + 136 + return NULL; 137 + } 138 + 139 + struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id) 140 + { 141 + struct ncsi_channel *nc, *tmp; 142 + int index; 143 + unsigned long flags; 144 + 145 + nc = kzalloc(sizeof(*nc), GFP_ATOMIC); 146 + if (!nc) 147 + return NULL; 148 + 149 + nc->id = id; 150 + nc->package = np; 151 + nc->state = NCSI_CHANNEL_INACTIVE; 152 + spin_lock_init(&nc->lock); 153 + for (index = 0; index < NCSI_CAP_MAX; index++) 154 + nc->caps[index].index = index; 155 + for (index = 0; index < NCSI_MODE_MAX; index++) 156 + nc->modes[index].index = index; 157 + 158 + spin_lock_irqsave(&np->lock, flags); 159 + tmp = ncsi_find_channel(np, id); 160 + if (tmp) { 161 + spin_unlock_irqrestore(&np->lock, flags); 162 + kfree(nc); 163 + return tmp; 164 + } 165 + 166 + list_add_tail_rcu(&nc->node, &np->channels); 167 + np->channel_num++; 168 + spin_unlock_irqrestore(&np->lock, flags); 169 + 170 + return nc; 171 + } 172 + 173 + static void ncsi_remove_channel(struct ncsi_channel *nc) 174 + { 175 + struct ncsi_package *np = nc->package; 176 + struct ncsi_channel_filter *ncf; 177 + unsigned long flags; 178 + int i; 179 + 180 + /* Release filters */ 181 + spin_lock_irqsave(&nc->lock, flags); 182 + for (i = 0; i < NCSI_FILTER_MAX; i++) { 183 + ncf = nc->filters[i]; 184 + if (!ncf) 185 + continue; 186 + 187 + nc->filters[i] = NULL; 188 + kfree(ncf); 189 + } 190 + 191 + nc->state = NCSI_CHANNEL_INACTIVE; 192 + spin_unlock_irqrestore(&nc->lock, flags); 193 + 194 + /* Remove and free channel */ 195 + spin_lock_irqsave(&np->lock, flags); 196 + list_del_rcu(&nc->node); 197 + np->channel_num--; 198 + spin_unlock_irqrestore(&np->lock, flags); 199 + 200 + kfree(nc); 201 + } 202 + 203 + struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp, 204 + unsigned char id) 205 + { 206 + struct ncsi_package *np; 207 + 208 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 209 + if (np->id == id) 210 + return np; 211 + } 212 + 213 + return NULL; 214 + } 215 + 216 + struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp, 217 + unsigned char id) 218 + { 219 + struct ncsi_package *np, *tmp; 220 + unsigned long flags; 221 + 222 + np = kzalloc(sizeof(*np), GFP_ATOMIC); 223 + if (!np) 224 + return NULL; 225 + 226 + np->id = id; 227 + np->ndp = ndp; 228 + spin_lock_init(&np->lock); 229 + INIT_LIST_HEAD(&np->channels); 230 + 231 + spin_lock_irqsave(&ndp->lock, flags); 232 + tmp = ncsi_find_package(ndp, id); 233 + if (tmp) { 234 + spin_unlock_irqrestore(&ndp->lock, flags); 235 + kfree(np); 236 + return tmp; 237 + } 238 + 239 + list_add_tail_rcu(&np->node, &ndp->packages); 240 + ndp->package_num++; 241 + spin_unlock_irqrestore(&ndp->lock, flags); 242 + 243 + return np; 244 + } 245 + 246 + void ncsi_remove_package(struct ncsi_package *np) 247 + { 248 + struct ncsi_dev_priv *ndp = np->ndp; 249 + struct ncsi_channel *nc, *tmp; 250 + unsigned long flags; 251 + 252 + /* Release all child channels */ 253 + list_for_each_entry_safe(nc, tmp, &np->channels, node) 254 + ncsi_remove_channel(nc); 255 + 256 + /* Remove and free package */ 257 + spin_lock_irqsave(&ndp->lock, flags); 258 + list_del_rcu(&np->node); 259 + ndp->package_num--; 260 + spin_unlock_irqrestore(&ndp->lock, flags); 261 + 262 + kfree(np); 263 + } 264 + 265 + void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp, 266 + unsigned char id, 267 + struct ncsi_package **np, 268 + struct ncsi_channel **nc) 269 + { 270 + struct ncsi_package *p; 271 + struct ncsi_channel *c; 272 + 273 + p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id)); 274 + c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL; 275 + 276 + if (np) 277 + *np = p; 278 + if (nc) 279 + *nc = c; 280 + } 281 + 282 + /* For two consecutive NCSI commands, the packet IDs shouldn't 283 + * be same. Otherwise, the bogus response might be replied. So 284 + * the available IDs are allocated in round-robin fashion. 285 + */ 286 + struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven) 287 + { 288 + struct ncsi_request *nr = NULL; 289 + int i, limit = ARRAY_SIZE(ndp->requests); 290 + unsigned long flags; 291 + 292 + /* Check if there is one available request until the ceiling */ 293 + spin_lock_irqsave(&ndp->lock, flags); 294 + for (i = ndp->request_id; !nr && i < limit; i++) { 295 + if (ndp->requests[i].used) 296 + continue; 297 + 298 + nr = &ndp->requests[i]; 299 + nr->used = true; 300 + nr->driven = driven; 301 + if (++ndp->request_id >= limit) 302 + ndp->request_id = 0; 303 + } 304 + 305 + /* Fail back to check from the starting cursor */ 306 + for (i = 0; !nr && i < ndp->request_id; i++) { 307 + if (ndp->requests[i].used) 308 + continue; 309 + 310 + nr = &ndp->requests[i]; 311 + nr->used = true; 312 + nr->driven = driven; 313 + if (++ndp->request_id >= limit) 314 + ndp->request_id = 0; 315 + } 316 + spin_unlock_irqrestore(&ndp->lock, flags); 317 + 318 + return nr; 319 + } 320 + 321 + void ncsi_free_request(struct ncsi_request *nr) 322 + { 323 + struct ncsi_dev_priv *ndp = nr->ndp; 324 + struct sk_buff *cmd, *rsp; 325 + unsigned long flags; 326 + 327 + if (nr->enabled) { 328 + nr->enabled = false; 329 + del_timer_sync(&nr->timer); 330 + } 331 + 332 + spin_lock_irqsave(&ndp->lock, flags); 333 + cmd = nr->cmd; 334 + rsp = nr->rsp; 335 + nr->cmd = NULL; 336 + nr->rsp = NULL; 337 + nr->used = false; 338 + spin_unlock_irqrestore(&ndp->lock, flags); 339 + 340 + /* Release command and response */ 341 + consume_skb(cmd); 342 + consume_skb(rsp); 343 + } 344 + 345 + struct ncsi_dev *ncsi_find_dev(struct net_device *dev) 346 + { 347 + struct ncsi_dev_priv *ndp; 348 + 349 + NCSI_FOR_EACH_DEV(ndp) { 350 + if (ndp->ndev.dev == dev) 351 + return &ndp->ndev; 352 + } 353 + 354 + return NULL; 355 + } 356 + 357 + static void ncsi_request_timeout(unsigned long data) 358 + { 359 + struct ncsi_request *nr = (struct ncsi_request *)data; 360 + struct ncsi_dev_priv *ndp = nr->ndp; 361 + unsigned long flags; 362 + 363 + /* If the request already had associated response, 364 + * let the response handler to release it. 365 + */ 366 + spin_lock_irqsave(&ndp->lock, flags); 367 + nr->enabled = false; 368 + if (nr->rsp || !nr->cmd) { 369 + spin_unlock_irqrestore(&ndp->lock, flags); 370 + return; 371 + } 372 + spin_unlock_irqrestore(&ndp->lock, flags); 373 + 374 + /* Release the request */ 375 + ncsi_free_request(nr); 376 + } 377 + 378 + struct ncsi_dev *ncsi_register_dev(struct net_device *dev, 379 + void (*handler)(struct ncsi_dev *ndev)) 380 + { 381 + struct ncsi_dev_priv *ndp; 382 + struct ncsi_dev *nd; 383 + unsigned long flags; 384 + int i; 385 + 386 + /* Check if the device has been registered or not */ 387 + nd = ncsi_find_dev(dev); 388 + if (nd) 389 + return nd; 390 + 391 + /* Create NCSI device */ 392 + ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC); 393 + if (!ndp) 394 + return NULL; 395 + 396 + nd = &ndp->ndev; 397 + nd->state = ncsi_dev_state_registered; 398 + nd->dev = dev; 399 + nd->handler = handler; 400 + 401 + /* Initialize private NCSI device */ 402 + spin_lock_init(&ndp->lock); 403 + INIT_LIST_HEAD(&ndp->packages); 404 + ndp->request_id = 0; 405 + for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) { 406 + ndp->requests[i].id = i; 407 + ndp->requests[i].ndp = ndp; 408 + setup_timer(&ndp->requests[i].timer, 409 + ncsi_request_timeout, 410 + (unsigned long)&ndp->requests[i]); 411 + } 412 + 413 + spin_lock_irqsave(&ncsi_dev_lock, flags); 414 + list_add_tail_rcu(&ndp->node, &ncsi_dev_list); 415 + spin_unlock_irqrestore(&ncsi_dev_lock, flags); 416 + 417 + return nd; 418 + } 419 + EXPORT_SYMBOL_GPL(ncsi_register_dev); 420 + 421 + void ncsi_unregister_dev(struct ncsi_dev *nd) 422 + { 423 + struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); 424 + struct ncsi_package *np, *tmp; 425 + unsigned long flags; 426 + 427 + list_for_each_entry_safe(np, tmp, &ndp->packages, node) 428 + ncsi_remove_package(np); 429 + 430 + spin_lock_irqsave(&ncsi_dev_lock, flags); 431 + list_del_rcu(&ndp->node); 432 + spin_unlock_irqrestore(&ncsi_dev_lock, flags); 433 + 434 + kfree(ndp); 435 + } 436 + EXPORT_SYMBOL_GPL(ncsi_unregister_dev);